---
title: "Extract phone numbers from text in C#"
url: https://recursiveai.org/onecontact/cookbook/extract-phone-numbers-from-text/
description: "Extract phone numbers from text in C#: runnable OneContact example with real output, matching rules and practical variations."
version: "1.0.0"
---

# Extract phone numbers 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) · [GitHub](https://github.com/recursive-ai-oss/onecontact-csharp) · [Docs](https://recursiveai.org/onecontact/docs/) · [Download example (.zip)](https://recursiveai.org/onecontact/cookbook/extract-phone-numbers-from-text/extract-phone-numbers-from-text.zip)

Find phone numbers inside a message and return each number with its original text and character range.

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

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

```csharp
using RecursiveAI.OneContact;
using RecursiveAI.OneContact.Phones;
var text = "Call (202) 555-0123.";
foreach (var match in PhoneNumber.FindAll(
    text, Region.US,
    PhoneMatchLeniency.Valid))
    Console.WriteLine(match.Number.E164);
```

```text
+12025550123
```

`Possible` accepts plausible length; `Valid` adds numbering-metadata and surrounding-text checks; `ExactGrouping` also requires the documented digit grouping.

Unzip [the example](https://recursiveai.org/onecontact/cookbook/extract-phone-numbers-from-text/extract-phone-numbers-from-text.zip), then `dotnet run`.

## Full program

All sample input is written directly in the program.

```csharp
using RecursiveAI.OneContact;
using RecursiveAI.OneContact.Phones;

var text = "Call (202) 555-0123 or +44 20 7946 0123.";
foreach (var match in PhoneNumber.FindAll(text, Region.US, PhoneMatchLeniency.Valid))
{
    Console.WriteLine($"Raw: {match.RawString}");
    Console.WriteLine($"E.164: {match.Number.E164}");
    Console.WriteLine($"Span: {match.Start}..{match.End}");
}
```

`dotnet run` prints:

```text
Raw: (202) 555-0123
E.164: +12025550123
Span: 5..19
Raw: +44 20 7946 0123
E.164: +442079460123
Span: 23..39
```

A national number needs the correct default region; candidates that do not satisfy the chosen leniency are skipped rather than returned as valid matches.

## Variations

Require exact digit grouping from the numbering metadata:

```csharp
using RecursiveAI.OneContact;
using RecursiveAI.OneContact.Phones;
var text = "Call +44 20 7946 0123.";
foreach (var match in PhoneNumber.FindAll(
    text, Region.US, PhoneMatchLeniency.ExactGrouping))
    Console.WriteLine(match.RawString);
```

```text
+44 20 7946 0123
```

## Related recipes

- [Parse phone number](https://recursiveai.org/onecontact/cookbook/parse-phone-number/)
- [Validate phone number](https://recursiveai.org/onecontact/cookbook/validate-phone-number/)

## The short answer

Use `PhoneNumber.FindAll(text, region, leniency)` to find phone numbers in running text. Each result contains `RawString`, `Number`, `Start` and `End`. The snippet at the top prints one E.164 number. The full program extracts both the US and British numbers from a single synthetic sentence and prints their source ranges.

This API is part of the core `RecursiveAI.OneContact` package. You do not need the HTML extraction package to scan a string for phone numbers. The [phone matcher notes](https://recursiveai.org/onecontact/docs/phones/#matcher) describe the candidate shapes and the acceptance rules used by the matcher.

## How do I extract phone numbers from text?

Supply a region for national input and enumerate the matches. In the example, `(202) 555-0123` needs US context; `+44 20 7946 0123` carries its international calling code. The default region is context for interpreting national digits, not an instruction to rewrite every match into that region.

Keep the raw text if you need to show what was found. Keep `Number.E164` if a downstream system needs a normalized dialing representation. Those are different outputs: the raw value carries punctuation from the source, while E.164 gives the international number without an extension. An extension remains available on the parsed number.

`Start` is the beginning of the match and `End` is the position immediately after it. These offsets let an application highlight the original substring without searching for the formatted number, which may never appear literally in the input. The full program’s first range is `5..19`; it covers exactly the national number in the sentence, not the word `Call` or the following separator.

## How can I validate phone numbers in C#?

For extraction, choose `PhoneMatchLeniency.Valid` when candidates must satisfy the metadata’s validity rules and surrounding-text checks. `Possible` accepts a parsable candidate with plausible length. `StrictGrouping` and `ExactGrouping` add increasingly specific requirements about how digit groups are separated. The variation demonstrates the last option on a British number.

A stricter choice can omit a number a person wrote in an unusual grouping. That is a tradeoff in what the extractor accepts, not a confirmation that omitted text is not a phone number. Pick a level that fits whether the application is suggesting candidates for review or importing into a stricter field.

A metadata-valid match does not prove the line is assigned, reachable or owned by the person named nearby. Parsing and matching concern the text and numbering rules. The [validation recipe](https://recursiveai.org/onecontact/cookbook/validate-phone-number/) shows the validity properties independently of text extraction.

## How can I extract a number from a string in C#?

If the intended value is a phone number, use the matcher rather than collecting every digit in the string. Removing non-digits can join unrelated values, strip an extension’s boundary and lose the plus sign that supplies international context. It also gives no source ranges for separate matches.

The matcher rejects candidate shapes resembling slash-separated dates, timestamps, page ranges and unbalanced brackets. That filtering is part of its documented behavior, not a guarantee that arbitrary prose can never produce a false match. Review extracted candidates when the surrounding text matters. For records containing names and other contact fields, the [extraction documentation](https://recursiveai.org/onecontact/docs/extraction/) covers the separate rules-based contact extractor.
