> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loqate.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Phone Validation Best Practices

> Integrate phone validation into your application with code examples in JavaScript, PHP, and .NET

**Build custom phone validation workflows using the Loqate API. This guide covers implementation patterns and integration strategies for JavaScript, PHP, and .NET.**

For pre-built solutions, see [Tag integrations](/our-services/tag-setup-guide) or [platform integrations](/integrations/all-integrations). Security and configuration details are in [API Security](/loqate-basics/api-security#integration-options).

<Note>These examples demonstrate core integration patterns. Adapt error handling and configuration for your production environment.</Note>

## Architecture Decisions

**Client-side validation**\
Quick to implement and requires less technical expertise, but exposes your API key in client code. Mitigate with domain restrictions in account settings. Best for real-time form validation.

**Server-side validation**\
Keeps credentials secure and provides full control over validation logic. Best for backend workflows and batch processing.

## API Overview

Phone Validation uses a single endpoint that returns validation status, carrier information, and formatted numbers.

**Endpoint:** `POST /PhoneNumberValidation/Interactive/Validate/v2.20/json6.ws`

**Parameters:**

* `Key` - Your API key
* `Phone` - Phone number to validate
* `Country` - ISO country code (optional if number includes country prefix)

**Response:**

```json theme={null}
{
  "Items": [{
    "PhoneNumber": "+441244657333",
    "IsValid": "Yes",
    "NetworkName": "BT",
    "NetworkCountry": "GB",
    "NationalFormat": "01244 657333",
    "NumberType": "Landline"
  }]
}
```

## Basic Implementation

<CodeGroup>
  ```javascript JavaScript theme={null}
  const validatePhone = async (phone, country = "GB") => {
      const response = await fetch(
          'https://services.postcodeanywhere.co.uk/PhoneNumberValidation/Interactive/Validate/v2.20/json3.ws',
          {
              method: 'POST',
              headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
              body: new URLSearchParams({
                  Key: process.env.LOQATE_API_KEY,
                  Phone: phone,
                  Country: country
              })
          }
      );
      
      const data = await response.json();
      return data.Items[0];
  };
  ```

  ```php PHP theme={null}
  <?php
  function validatePhone($phone, $country = 'GB') {
      $ch = curl_init('https://services.postcodeanywhere.co.uk/PhoneNumberValidation/Interactive/Validate/v2.20/json3.ws');
      
      curl_setopt_array($ch, [
          CURLOPT_POST => true,
          CURLOPT_POSTFIELDS => http_build_query([
              'Key' => getenv('LOQATE_API_KEY'),
              'Phone' => $phone,
              'Country' => $country
          ]),
          CURLOPT_RETURNTRANSFER => true
      ]);
      
      $response = curl_exec($ch);
      curl_close($ch);
      
      return json_decode($response, true)['Items'][0];
  }
  ?>
  ```

  ```csharp .NET theme={null}
  public async Task<PhoneValidationResult> ValidatePhone(string phone, string country = "GB")
  {
      using var client = new HttpClient();
      
      var parameters = new Dictionary<string, string>
      {
          ["Key"] = Environment.GetEnvironmentVariable("LOQATE_API_KEY"),
          ["Phone"] = phone,
          ["Country"] = country
      };
      
      var response = await client.PostAsync(
          "https://services.postcodeanywhere.co.uk/PhoneNumberValidation/Interactive/Validate/v2.20/json3.ws",
          new FormUrlEncodedContent(parameters)
      );
      
      var data = await response.Content.ReadAsStringAsync();
      return JsonSerializer.Deserialize<PhoneValidationResponse>(data).Items[0];
  }
  ```
</CodeGroup>

## Handling Results

The `IsValid` field returns `"Yes"`, `"No"`, or `"Maybe"`. A `"Maybe"` response indicates temporary carrier network issues—treat it as potentially valid.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const result = await validatePhone("+441244657333");

  if (result.IsValid === "Yes") {
      console.log(`Valid ${result.NumberType}: ${result.NationalFormat}`);
  } else if (result.IsValid === "Maybe") {
      console.log("Validation incomplete - network unavailable");
  } else {
      console.log("Invalid number");
  }
  ```

  ```php PHP theme={null}
  <?php
  $result = validatePhone('+441244657333');

  if ($result['IsValid'] === 'Yes') {
      echo "Valid {$result['NumberType']}: {$result['NationalFormat']}";
  } elseif ($result['IsValid'] === 'Maybe') {
      echo "Validation incomplete - network unavailable";
  } else {
      echo "Invalid number";
  }
  ?>
  ```

  ```csharp .NET theme={null}
  var result = await ValidatePhone("+441244657333");

  if (result.IsValid == "Yes")
  {
      Console.WriteLine($"Valid {result.NumberType}: {result.NationalFormat}");
  }
  else if (result.IsValid == "Maybe")
  {
      Console.WriteLine("Validation incomplete - network unavailable");
  }
  else
  {
      Console.WriteLine("Invalid number");
  }
  ```
</CodeGroup>

## Error Handling

The API returns standard HTTP status codes. Handle errors based on your application's requirements.

| Status | Meaning      | Action                                   |
| ------ | ------------ | ---------------------------------------- |
| 400    | Bad request  | Check request parameters                 |
| 401    | Unauthorized | Verify API key                           |
| 403    | Forbidden    | Check service permissions or rate limits |
| 500    | Server error | Retry with exponential backoff           |

**Retry logic:**

```javascript theme={null}
const validateWithRetry = async (phone, country, maxRetries = 3) => {
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await validatePhone(phone, country);
        } catch (error) {
            if (error.status < 500 || i === maxRetries - 1) throw error;
            await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
        }
    }
};
```

## Integration Patterns

### Real-Time Form Validation

Validate phone numbers on blur with debouncing to reduce API calls.

```javascript theme={null}
let timeout;
phoneInput.addEventListener('blur', (e) => {
    clearTimeout(timeout);
    timeout = setTimeout(async () => {
        const result = await validatePhone(e.target.value);
        updateUI(result.IsValid === "Yes");
    }, 500);
});
```

### Batch Validation

Process multiple numbers with rate limiting.

```php theme={null}
function validateBatch($phones, $country = 'GB') {
    $results = [];
    foreach ($phones as $phone) {
        $results[] = [
            'phone' => $phone,
            'result' => validatePhone($phone, $country)
        ];
        usleep(100000); // 100ms delay between requests
    }
    return $results;
}
```

### Multi-Country Support

Auto-detect user location and adjust validation accordingly.

```javascript theme={null}
const detectCountry = async () => {
    const response = await fetch('https://ipapi.co/json/');
    const data = await response.json();
    return data.country_code;
};

const country = await detectCountry();
const result = await validatePhone(phoneNumber, country);
```

## Data Storage

Store validated numbers in international format for consistency.

```sql theme={null}
CREATE TABLE phone_numbers (
    id SERIAL PRIMARY KEY,
    international VARCHAR(20),
    national VARCHAR(20),
    country_code VARCHAR(3),
    number_type VARCHAR(10),
    is_valid BOOLEAN,
    validated_at TIMESTAMP DEFAULT NOW()
);
```

## Testing

Test with numbers in various formats and countries:

| Number        | Country | Expected | Type     |
| ------------- | ------- | -------- | -------- |
| +441244657333 | GB      | Valid    | Landline |
| +447700900123 | GB      | Valid    | Mobile   |
| +14155552671  | US      | Valid    | Mobile   |
| +441234567    | GB      | Invalid  | N/A      |

## Security

Configure API key restrictions in your account settings:

* Add domain restrictions for client-side implementations
* Use IP allowlists for server-side implementations
* Set rate limits to prevent abuse
* Rotate keys periodically

See [API Security](/loqate-basics/api-security) for detailed guidance.

## Additional Resources

* [Implementation Guide](/our-services/phone-validation/implementation-guide)
* [API Reference](/api-reference/phone-validation/individual-validate)
* [Quickstart](/our-services/phone-validation/phone-validation-quick-start)
* [Coverage Information](/our-services/phone-validation/overview)
