PrecisionGEO REST API returns JSON responses consistently across all endpoints. Every record returns three layers of identification simultaneously: a PrecisionGEO integer id, an ISO standard code, and the human-readable name. This is a deliberate design decision — clients should never have to choose between machine-readable keys and human-readable display values.
Why three identifiers on every record?
id (integer) — PrecisionGEO Record ID: Unique, immutable integer assigned by PrecisionGEO. Store this in your own database as a foreign key for lookups, joins, and single-record fetches via GET /api/v1/city/59020. Unambiguous — there are multiple cities named "Salem", "Nagar", and "Rampur" across Indian states; the id is the only field that uniquely identifies a specific city with no room for error.
iso2 / iso3 / code — ISO Standard Code: Industry-standard codes required for interoperability — GST APIs, payment gateways, logistics partners, government portals, and most third-party systems speak ISO codes, not free text. Pass IN-MH to a shipping API; pass IN to a currency conversion API — these codes work universally.
name — Human-Readable Display Text: What your users see in dropdowns, forms, reports, and emails. Store this if your application needs to display it without an additional API lookup — but always store the id or code alongside it as the canonical key.
Response envelope structure:
{
"success": true,
"count": 36,
"data": [ ... ],
"cached": true,
"generated_at": "2025-09-01T10:30:00Z"
}
Example: GET /api/v1/countries (abbreviated)
{
"success": true,
"count": 250,
"data": [
{
"id": 101,
"iso2": "IN",
"iso3": "IND",
"name": "India",
"phone_code": "+91",
"currency": "INR",
"currency_name":"Indian Rupee",
"flag": "🇮🇳",
"continent": "Asia"
},
{
"id": 233,
"iso2": "US",
"iso3": "USA",
"name": "United States",
"phone_code": "+1",
"currency": "USD",
"currency_name":"US Dollar",
"flag": "🇺🇸",
"continent": "North America"
}
]
}
Example: GET /api/v1/states?country=IN (abbreviated)
{
"success": true,
"count": 36,
"data": [
{ "id": 4008, "code": "IN-MH", "name": "Maharashtra", "type": "state", "country_id": 101, "country_iso2": "IN" },
{ "id": 4013, "code": "IN-DL", "name": "Delhi", "type": "union_territory", "country_id": 101, "country_iso2": "IN" },
{ "id": 4041, "code": "IN-LA", "name": "Ladakh", "type": "union_territory", "country_id": 101, "country_iso2": "IN" },
{ "id": 4035, "code": "IN-TG", "name": "Telangana", "type": "state", "country_id": 101, "country_iso2": "IN" }
]
}
Example: GET /api/v1/cities?state=IN-MH (abbreviated)
{
"success": true,
"count": 534,
"data": [
{ "id": 59010, "name": "Mumbai", "state_id": 4008, "state_code": "IN-MH", "district": "Mumbai City", "tier": 1, "lat": 19.0760, "lng": 72.8777 },
{ "id": 59020, "name": "Pune", "state_id": 4008, "state_code": "IN-MH", "district": "Pune", "tier": 1, "lat": 18.5204, "lng": 73.8567 },
{ "id": 59031, "name": "Nashik", "state_id": 4008, "state_code": "IN-MH", "district": "Nashik", "tier": 2, "lat": 20.0059, "lng": 73.7898 },
{ "id": 59044, "name": "Kolhapur", "state_id": 4008, "state_code": "IN-MH", "district": "Kolhapur", "tier": 2, "lat": 16.7050, "lng": 74.2433 }
]
}
Single-record lookup by ID:
GET /api/v1/city/59020
→ { "id": 59020, "name": "Pune", "state_id": 4008, "state_code": "IN-MH",
"country_id": 101, "country_iso2": "IN", "district": "Pune",
"tier": 1, "lat": 18.5204, "lng": 73.8567 }
GET /api/v1/state/4008
→ { "id": 4008, "code": "IN-MH", "name": "Maharashtra",
"type": "state", "country_id": 101, "country_iso2": "IN" }
GET /api/v1/country/101
→ { "id": 101, "iso2": "IN", "iso3": "IND", "name": "India", ... }
Recommended storage pattern in your database:
- Store
country_id, state_id, and city_id (PrecisionGEO integers) as foreign-key columns in your customer/order/address table
- Optionally cache
country_iso2, state_code, and city_name as denormalised text columns for display — avoids an API call on every read
- Use the integer IDs for joins, filters, and aggregations — text names are for display only
- The PrecisionGEO
id is immutable — it will never change for a given record, making it safe as a long-term foreign key in your own database
HTTP status codes:
200 OK — successful response
400 Bad Request — missing or invalid parameters
401 Unauthorized — missing or invalid JWT/API key
403 Forbidden — CORS origin not whitelisted, or key does not have access to requested resource
404 Not Found — country/state/city ID or code not found in database
429 Too Many Requests — rate limit exceeded; Retry-After header included
500 Internal Server Error — unexpected server error (extremely rare; monitored 24×7)