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.
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.
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
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.