API Reference
Authentication
The Addresspenny API uses Bearer token authentication. Include your API token in the Authorization header with every request.
Authorization: Bearer your_api_token
You can create and manage API tokens from the API Tokens page. See the Authentication guide for details.
Base URL
https://addresspenny.com/api/v1
All endpoints are scoped to an account: /api/v1/accounts/:account_id/...
Addresses
Validate an address
Submits an address for validation. The address is created with a pending status while validation runs asynchronously in the background. Fetch the address again to check when the result is ready.
POST /api/v1/accounts/:account_id/addresses
Request body:
{
"address": {
"original_input": "30 rockefeller plaza new york ny"
}
}
The original_input can be a full or partial address as a single string. The validation service will parse and standardize it.
Pass "sync": true to validate inline. The response will include the final status and remote_payload immediately instead of pending. Useful for agents and interactive tools that need an answer in one round-trip.
Response: 201 Created
{
"address": {
"id": 1,
"original_input": "30 rockefeller plaza new york ny",
"status": "pending",
"created_at": "2026-03-31T12:00:00Z",
"updated_at": "2026-03-31T12:00:00Z"
}
}
Get an address
Returns a single address. Once validation completes, the remote_payload contains the full result.
GET /api/v1/accounts/:account_id/addresses/:id
Response: 200 OK
{
"address": {
"id": 1,
"original_input": "30 rockefeller plaza new york ny",
"status": "validated",
"remote_payload": {
"address": {
"city": "New York",
"line1": "30 Rockefeller Plaza",
"line2": null,
"state": "NY",
"country": "US",
"postal_code": "10112-0015"
},
"is_valid": false,
"original_address": {
"city": "",
"lines": ["30 rockefeller plaza new york ny"],
"state": "",
"country": "US",
"postal_code": ""
},
"formatted_address": "30 Rockefeller Plaza, New York, NY 10112-0015, USA",
"validation_results": {
"messages": [
{
"code": "street_number.confirmed",
"text": "Street number confirmed",
"type": "info",
"source": "google_maps"
},
{
"code": "route.confirmed",
"text": "Route confirmed",
"type": "info",
"source": "google_maps"
},
{
"code": "locality.confirmed",
"text": "Locality confirmed",
"type": "info",
"source": "google_maps"
},
{
"code": "postal_code.confirmed",
"text": "Postal code confirmed",
"type": "info",
"source": "google_maps"
}
],
"granularity": "premise"
}
},
"created_at": "2026-03-31T12:00:00Z",
"updated_at": "2026-03-31T12:00:05Z"
}
}
List addresses
Returns all addresses for the account, most recent first.
GET /api/v1/accounts/:account_id/addresses
Response: 200 OK
{
"addresses": [
{
"id": 2,
"original_input": "350 Fifth Avenue, New York, NY",
"status": "pending",
"created_at": "2026-03-31T12:01:00Z",
"updated_at": "2026-03-31T12:01:00Z"
},
{
"id": 1,
"original_input": "30 rockefeller plaza new york ny",
"status": "validated",
"remote_payload": { "..." : "..." },
"created_at": "2026-03-31T12:00:00Z",
"updated_at": "2026-03-31T12:00:05Z"
}
]
}
Batch validate addresses
Submit multiple addresses for validation in a single request. Each address consumes one credit. Maximum batch size is 100.
POST /api/v1/accounts/:account_id/addresses/batch
Required scope: addresses:write
Request body:
{
"addresses": [
"1600 Amphitheatre Pkwy, Mountain View, CA",
"350 Fifth Avenue, New York, NY",
"1060 W Addison St, Chicago, IL"
]
}
Response: 201 Created
{
"addresses": [
{
"id": 10,
"original_input": "1600 Amphitheatre Pkwy, Mountain View, CA",
"status": "pending",
"created_at": "2026-04-04T12:00:00Z",
"updated_at": "2026-04-04T12:00:00Z"
},
{
"id": 11,
"original_input": "350 Fifth Avenue, New York, NY",
"status": "pending",
"created_at": "2026-04-04T12:00:00Z",
"updated_at": "2026-04-04T12:00:00Z"
},
{
"id": 12,
"original_input": "1060 W Addison St, Chicago, IL",
"status": "pending",
"created_at": "2026-04-04T12:00:00Z",
"updated_at": "2026-04-04T12:00:00Z"
}
]
}
Individual addresses that fail validation (e.g., blank input) will include an errors array instead of the address fields. Successfully created addresses in the same batch are not affected.
Pass "sync": true to validate the batch inline. Each address in the response will return with its final status instead of pending.
Parse and validate (AI)
Extracts postal addresses from unstructured text using AI, then validates each one synchronously. Useful for cleaning freeform input from chats, CRM notes, transcripts, or scraped pages.
POST /api/v1/accounts/:account_id/addresses/parse_and_validate
Required scope: addresses:write
Request body:
{
"text": "Please ship to our NY office at 350 Fifth Ave, New York, NY and the Chicago branch at 1060 W Addison St, Chicago, IL."
}
Response: 201 Created — same shape as batch, with addresses already validated inline.
Each extracted and validated address consumes one credit (same as batch). If no valid addresses are found in the text, the response contains an empty addresses array and no credits are consumed.
On extraction failure (AI provider unavailable or errors), the response is 502 Bad Gateway with error.code = "extraction_failed".
MCP server
Addresspenny ships an official Model Context Protocol server so Claude Desktop, Cursor, and other MCP-compatible agents can call the validation engine directly — no custom integration code.
Scopes
API tokens can be created with specific permission scopes. Tokens with no scopes have full access (legacy behavior). Available scopes:
addresses:read— list and get addressesaddresses:write— create and batch validate addresseswebhooks:read— list webhook subscriptionswebhooks:manage— create and delete webhook subscriptionsaccount:read— read account information
Requests with insufficient scope receive 403 Forbidden with details about the required and granted scopes.
Rate limits
API requests are rate limited per token:
- Read endpoints (index, show): 120 requests per minute
- Write endpoints (create, batch): 60 requests per minute
- AI endpoints (parse_and_validate): 20 requests per minute
When rate limited, the API returns 429 Too Many Requests:
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
}
Idempotency
For safe retries, include an Idempotency-Key header with any POST request. If the same key is sent again with the same request body, the API returns the original cached response without re-processing.
curl -X POST https://addresspenny.com/api/v1/accounts/acct_1a2b3c/addresses \
-H "Authorization: Bearer your_api_token" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{"address": {"original_input": "1600 Amphitheatre Pkwy, Mountain View, CA"}}'
- Keys expire after 24 hours
- Reusing a key with a different request body returns
409 Conflict - The header is optional — omit it for normal (non-idempotent) behavior
- Only applies to POST endpoints (GET requests are naturally idempotent)
Response fields
Address object
id— unique address identifieroriginal_input— the raw string you submittedstatus—pending,validated, orfailedremote_payload— validation results (only present whenvalidated)created_at/updated_at— timestamps
Validation payload
The remote_payload contains the parsed and validated address data:
is_valid— whether the address is fully deliverable. An address can have all components confirmed but still befalseif something is missing (e.g., an apartment or suite number).formatted_address— the standardized, single-line addressaddress— the parsed address components (line1,line2,city,state,postal_code,country)original_address— how the validation service interpreted your raw input before processingvalidation_results.messages— component-level confirmation or warning messages from Google Maps, each with acode,text, andtype(info,warning, orerror)validation_results.granularity— how precisely the address was matched:premise(exact building),sub_premise(unit level),route(street level only), etc.
Address status
pending— validation is in progress (typically completes in a few seconds)validated— validation complete,remote_payloadcontains resultsfailed— validation could not be completed (e.g., service unavailable)
Errors
The API returns structured error objects with a code and message.
Insufficient credits (402)
Returned when the account has no remaining address credits. Each validation consumes one credit.
{
"error": {
"code": "insufficient_credits",
"message": "No address credits remaining",
"credits_remaining": 0,
"credits_limit": 50
}
}
Validation failed (422)
Returned when the request body is invalid. Includes a details array with per-field errors.
{
"error": {
"code": "validation_failed",
"message": "Original input can't be blank",
"details": [
{ "field": "original_input", "message": "can't be blank" }
]
}
}
Insufficient scope (403)
Returned when the API token does not have the required permission scope.
{
"error": {
"code": "insufficient_scope",
"message": "Token does not have the required scope: addresses:write",
"required_scope": "addresses:write",
"granted_scopes": ["addresses:read", "account:read"]
}
}
Rate limited (429)
Returned when the token has exceeded the request rate limit.
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
}
Idempotency conflict (409)
Returned when an Idempotency-Key is reused with a different request body.
{
"error": {
"code": "idempotency_conflict",
"message": "Idempotency key already used with different request body"
}
}
Unauthorized (401)
Returned when the API token is missing or invalid.