Skip to main content
GET
Page through a country’s postcodes / ZIP codes, or narrow the list with a code/locality search, state, city, or type filter. Use this to build postcode discovery instead of downloading the full dataset.
Requires a Starter+ plan — including a bare list request with no filters at all. This is intentional: it stops a Community or Legacy key from paginating through and scraping the full hosted dataset. Get Postcode by Code (exact lookup) has no such gate and stays free on every plan.
This is informational postcode data, not an address verification or mail-deliverability service. Results confirm codes exist in the dataset — they do not confirm delivery points or standardize addresses.
Trim results: Add ?fields= to limit the columns returned. Requires Starter+. There is no ?sort= on this endpoint — results are always ordered by code, then id, so cursor pagination stays stable. See the Field Filtering & Sorting guide for ?fields= syntax.
There is no total-count field. Counting every match up front would mean a second expensive query on top of a 844k-row table, so check has_more and keep paging instead of expecting a total.
200 - Search with q, Basic tier (Starter)
200 - Search with q, Full tier (Supporter, Professional, Business)

Tier-based field availability

Community and Legacy lack the searchEndpoint feature this endpoint requires, so the Basic row below is only reachable on Starter — Community and Legacy get a 403 on this endpoint before field access matters. See Pricing for plan details.

Caching and conditional requests

Every response sets ETag, Cache-Control (public, max-age=3600), and X-Cache (HIT/MISS), cached for 1 hour regardless of whether q is present. Send the last ETag back as If-None-Match to get a bodyless 304 when nothing changed instead of re-downloading the page:

Errors

400 - Invalid country code
400 - Cursor filters don't match this request
A cursor also 400s as "cursor belongs to a different country." when reused across countries, "cursor is invalid or has been tampered with." when it’s malformed or its signature doesn’t verify, and "cursor has expired. Start again without a cursor." after 24 hours.
403 - Requires Starter+
This 403 fires on every request to this endpoint from a Community or Legacy key — including a bare list with no q, state_code, city_id, or type at all. Only exact lookup (Get Postcode by Code) is free on those plans.
200 - Country with no postcode coverage
A country that exists but has no postcodes, or a filter that matches nothing, returns 200 with an empty data array — not a 404. The 404 cases (country_not_found) match Get Postcode by Code for an unknown country. See Errors & Rate Limits for the 401/429 shapes and usage headers common to every endpoint.
This endpoint requires a Starter+ plan, for listing as well as searching. Compare plans to add it to your API key.

Get Postcode by Code

Exact lookup, free on every plan — start here before adding search.

Postcodes Reference

Coverage stats, postcode types, and self-hosted query examples.

Authorizations

X-CSCAPI-KEY
string
header
required

API key for authentication. Get your free key at app.countrystatecity.in.

Path Parameters

ciso
string
required

ISO 3166-1 alpha-2 country code, case-insensitive (e.g. LK). Numeric IDs and ISO3 codes are not accepted on postcode endpoints.

Required string length: 2
Example:

"LK"

Query Parameters

q
string

Case-insensitive search: codes starting with q, or locality names containing it. 2 to 100 characters.

Required string length: 2 - 100
Example:

"1100"

state_code
string

Exact, case-insensitive state/province code filter.

Example:

"DL"

city_id
integer

Exact city ID filter (positive integer).

Required range: x >= 1
Example:

129881

type
string

Exact, case-insensitive postcode type filter. Not a fixed enum; unrecognised values match zero rows.

Maximum string length: 32
Example:

"full"

fields
string

Comma-separated fields to include. id is always included. There is no sort on this endpoint.

Example:

"code,locality_name"

limit
integer
default:50

Records per page, 1 to 100.

Required range: 1 <= x <= 100
cursor
string

Opaque pagination.next_cursor from the previous page. Resend the same filters, or omit them and send only the cursor.

Response

A page of postcodes

data
object[]
required

Matching records for this page, ordered by code then id

pagination
object
required