---
title: "Extract email addresses from text in C#"
url: https://recursiveai.org/onecontact/cookbook/extract-emails-from-text/
description: "Extract email addresses from text in C# with OneContact: free, open-source .NET code, a runnable example and real output."
version: "1.0.0"
---

# Extract email addresses from text in C#

**OneContact** is a free, open-source contact data library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies, no network access.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneContact.Extraction) · [GitHub](https://github.com/recursive-ai-oss/onecontact-csharp) · [Docs](https://recursiveai.org/onecontact/docs/)

Find email addresses in prose.

```shell
dotnet add package RecursiveAI.OneContact.Extraction
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneContact.Extraction;
using RecursiveAI.OneContact.Extraction.Records;
var extractor = new ContactExtractor();
var result = extractor.ExtractText(
    "Email jane@example.com.");
foreach (var field in result.Fields)
    if (field.Kind == FieldKind.Email)
        Console.WriteLine(field.Text);
```

```text
jane@example.com
```

**[Download this example (.zip)](https://recursiveai.org/onecontact/cookbook/extract-emails-from-text/extract-emails-from-text.zip)**: unzip, then `dotnet run`.

## Full program

```csharp
using RecursiveAI.OneContact.Extraction;
using RecursiveAI.OneContact.Extraction.Records;

string text = "Email jane@example.com or help [at] example [dot] com.";
var result = new ContactExtractor().ExtractText(text);
foreach (var field in result.Fields)
{
    if (field.Kind != FieldKind.Email) continue;
    Console.WriteLine($"Raw: {field.Raw}");
    Console.WriteLine($"Email: {field.Text}");
    if (field.Normalized.TryGetEmail(out var email))
        Console.WriteLine($"Canonical: {email.Canonical}");
    else
        Console.WriteLine("Canonical: unavailable");
}
```

`dotnet run` prints:

```text
Raw: jane@example.com
Email: jane@example.com
Canonical: jane@example.com
Raw: help [at] example [dot] com
Email: help@example.com
Canonical: help@example.com
```

The full program keeps the raw slice beside the decoded address. Input over the extractor’s input limits throws `ContactExtractionException`.

## Variations

Read an email address from an HTML link instead of prose:

```csharp
using RecursiveAI.OneContact.Extraction;
using RecursiveAI.OneContact.Extraction.Records;

var result = new ContactExtractor().ExtractHtml(
    """<a href="mailto:jane@example.com">Email Jane</a>""");
foreach (var field in result.Fields)
    if (field.Kind == FieldKind.Email)
        Console.WriteLine(field.Text);
```

```text
jane@example.com
```

## Related recipes

- [Validate a phone number in C#](https://recursiveai.org/onecontact/cookbook/validate-phone-number/)
- [Format a phone number as E.164 in C#](https://recursiveai.org/onecontact/cookbook/format-phone-number-e164/)
- [Validate an email address in C#](https://recursiveai.org/onecontact/cookbook/validate-email-address/)
- [Parse a phone number in C#](https://recursiveai.org/onecontact/cookbook/parse-phone-number/)

## The short answer

Call `ContactExtractor.ExtractText`, then filter `result.Fields` to `FieldKind.Email`. This scans prose rather than requiring the whole string to be one mailbox. The extraction package references the core contact types and returns both source text and normalized values.

The example includes a plain address and an obfuscated one. Both are literal strings visible in `Program.cs`, so you can change the input and run the project without preparing a file. The printed output shows the original slice, decoded email text and canonical value for each field.

## How can I extract email addresses from text?

Pass the entire string to `ExtractText`. The flat `Fields` collection is useful when all you want is email addresses: it includes fields regardless of whether they were assigned to a person or organization record. Filtering only named records could miss an email that was found but not assigned to a named contact.

Each field has a different role for its values. `Raw` is the original source slice. `Text` is the decoded text given to normalization. `Normalized` holds the core parsed value when normalization succeeded. It is a value type, not a nullable reference; `TryGetEmail` checks whether it contains a parsed email before the full program reads its canonical form. The full program prints all three so the transformation is visible, especially for the bracketed obfuscation.

Use `Span` if you need to highlight a match in the input. For string input, offsets count UTF-16 code units. For byte input to the HTML reader, they count bytes of the original input through the parser’s offset map. Don’t apply string indexes to a byte buffer or vice versa. These offset rules are documented in the [extraction notes](https://recursiveai.org/onecontact/docs/extraction/).

The reader also understands documented obfuscation patterns, including bracketed “at” and “dot”. That is rules-based decoding, not a promise to recover every address a person can disguise. Preserve `Raw` when you want an import review screen to explain what was found. The extraction result keeps source information rather than silently rewriting the original document.

The HTML variation uses `ExtractHtml` instead of treating markup as prose. That lets the markup reader recognize a `mailto:` link even when the visible link label is only “Email Jane”. Choose the entry point that matches the input you actually have. Neither entry point fetches a web page for you.

## How can I validate email addresses in C#?

Extraction finds candidates inside a larger input; parsing checks one mailbox. Use `EmailAddress.TryParse` for a form field or for a single candidate you need to handle separately. The [validation recipe](https://recursiveai.org/onecontact/cookbook/validate-email-address/) shows a complete runnable version. Successful syntax parsing is not mailbox delivery verification.

For an extraction workflow, retain the field’s source and normalization information when deciding which rows to accept. The result model preserves a field even when a core normalizer cannot normalize it. A visible candidate is therefore not automatically a clean, deliverable contact record. Decide how your application handles unnormalized fields and duplicates before exporting a mailing list.

The extractor can also return phones, websites and other contact fields, so filtering is essential when the requested output is emails only. Don’t assume every field is an email just because the input was a short signature. The explicit `FieldKind.Email` check makes the example safe to extend to longer notes containing several kinds of contact data.

As the [OneContact release overview](https://recursiveai.org/onecontact/docs/overview/) states: “Version 1.0 runs on rules and markup alone.”
