Skip to main content

POST /api/validate-bulk

Validate up to 1,000 postal codes in a single request. Mixed countries supported. Same flat per-record price as /api/validate ($0.000028 per record) — no bulk discount or premium. Per-record errors are reported in the results array, not as request-level failures.

tip

"Validate 1, validate 1,000 — flat rate, every record." Most postal APIs charge a premium for bulk operations. PostalDataPI charges the same per-record price whether you send one or a thousand.

Field naming note

The bulk endpoint uses modern field namespostalCode and countryCode. The single-record endpoints (/api/lookup, /api/validate, /api/city, /api/metazip) preserve the legacy zipcode and country naming for backward compatibility.

Request

POST https://postaldatapi.com/api/validate-bulk
Content-Type: application/json

Body Parameters

ParameterTypeRequiredDescription
apiKeystringYesYour API key
recordsarrayYesArray of {postalCode, countryCode} objects. Max 1,000. Mixed countries supported.

Per-record fields

FieldTypeRequiredDescription
postalCodestringYesThe postal code to validate
countryCodestringYesISO 3166-1 alpha-2 country code (e.g., "US", "GB", "DE")

Response

Success (200)

{
"results": [
{ "postalCode": "90210", "countryCode": "US", "valid": true, "normalized": "90210", "reason": null },
{ "postalCode": "SW1A 1AA", "countryCode": "GB", "valid": true, "normalized": "SW1A", "reason": null },
{ "postalCode": "FOO99", "countryCode": "US", "valid": false, "normalized": null, "reason": "not_found" },
{ "postalCode": "12345", "countryCode": "ZZ", "valid": false, "normalized": null, "reason": "unknown_country" }
],
"totalCost": 0.000112,
"balance": 4.998888,
"performance": { "totalTime": "8ms" },
"rateLimit": { "enabled": false }
}

Per-record fields

FieldTypeDescription
postalCodestringEchoed from the input record
countryCodestringEchoed from the input record (uppercased)
validbooleanWhether the postal code exists in the country's dataset
normalizedstring or nullCanonical key when valid (e.g. "SW1A" for GB outcode-only datasets, FSA prefix for CA), null when invalid
reasonstring or nullWhy the record is invalid: null when valid, otherwise "not_found", "invalid_format", or "unknown_country"

Top-level fields

FieldTypeDescription
resultsarrayPer-record results, in the same order as the request
totalCostnumberTotal cost of this request in USD (N records × per-query rate)
balancenumberAccount balance after this request was charged
performanceobjectTiming breakdown
rateLimitobjectCurrent rate limit status

Limits

  • Max records per request: 1,000. Above that, split into multiple calls.
  • Cost: N × $0.000028 per request. Flat rate; no bulk discount or premium.
  • Rate limits: N records count as N queries against your per-minute / per-hour / per-day windows. A bulk request of 100 records consumes 100 from your rate-limit budget.
  • Sync only: the endpoint blocks until all N records are processed (typically < 1 second for 1,000 records). No async / webhook callback in v1.

cURL Example

curl -X POST https://postaldatapi.com/api/validate-bulk \
-H "Content-Type: application/json" \
-d '{
"apiKey": "YOUR_API_KEY",
"records": [
{"postalCode": "90210", "countryCode": "US"},
{"postalCode": "SW1A 1AA", "countryCode": "GB"},
{"postalCode": "10115", "countryCode": "DE"}
]
}'

Errors

StatusErrorCause
400Missing required field: apiKeyNo apiKey in request body
400Missing required field: records (must be an array)records missing or not an array
400records array must contain at least 1 recordEmpty array
400records array exceeds maximum of 1000 per requestMore than 1,000 records
400records[N].postalCode must be a non-empty stringA record has invalid/missing postalCode
400records[N].countryCode must be a 2-letter ISO 3166-1 alpha-2 codeA record has invalid/missing countryCode
401Invalid API keyAPI key does not exist or was revoked
402Insufficient balance for bulk requestBalance is less than N × $0.000028 — entire request rejected, no partial billing
429Rate limit would be exceeded by this bulk requestAdding N records would exceed your per-minute/hour/day limit

SDK Examples

from postaldatapi import PostalDataPI

client = PostalDataPI(api_key="YOUR_API_KEY")

result = client.validate_bulk([
{"postal_code": "90210", "country_code": "US"},
{"postal_code": "SW1A 1AA", "country_code": "GB"},
{"postal_code": "FOO99", "country_code": "US"},
])

for r in result.results:
if r.valid:
print(f"{r.postal_code} ({r.country_code}) -> {r.normalized}")
else:
print(f"{r.postal_code} ({r.country_code}) INVALID: {r.reason}")

print(f"Cost: ${result.total_cost:.6f}")
print(f"Balance: ${result.balance:.6f}")

Common use cases

  • CRM data hygiene. Validate a list of customer postal codes from your CRM in a single batch.
  • Form pre-submission cleanup. Bulk-validate a CSV of addresses uploaded by a user before kicking off downstream geocoding.
  • Ongoing address-list maintenance. Periodic validation of a marketing list to flag entries that have become invalid.

For per-record geographic data (city, coordinates, county, etc.), use /api/lookup or /api/metazip on individual records after bulk validation.