Skip to main content
Official JS/TS client for the live Country State City REST API — countries, states, cities, regions, currencies, phone codes, timezones, fuzzy search, and account usage. Zero runtime dependencies, dual ESM/CJS builds, full TypeScript types.
This is not the same thing as the npm Packages covered elsewhere on this site. Those embed the geographical data directly — no API key, no network call. @countrystatecity/sdk is the opposite: a live REST API client. It needs an API key, makes real HTTP requests, and is subject to your plan’s usage quotas — in exchange, it’s always current and covers endpoints the offline packages don’t have (search, usage, and live currency/timezone/phone lookups). You can use both together — see Migrating from the local packages below.
@countrystatecity/sdk is currently 0.1.0, pre-1.0. The API surface may still shift before a stable 1.0 release.

Installation

Requires Node.js 18+ (for native fetch) or any modern browser. Zero runtime dependencies — built entirely on native fetch, URL/URLSearchParams, and AbortController. You’ll need an API key — get one free and compare plans and quotas.

Quick Start

Every resource method returns { data, meta }data contains the typed result and meta carries request, usage, pagination, and cache information alongside it. See Response Metadata below.

Configuration

The SDK never reads apiKey from an environment variable and never writes it to disk — pass it explicitly at construction time.

Resource Reference

Every list/get/etc. method also accepts a trailing { signal?, timeout?, headers? } for per-call overrides — see Retries & Timeouts below.
csc.cities.list({ state }) requires country to also be set — a ValidationError is thrown client-side otherwise, before any network call.

Error Handling

All errors extend CSCError (message, statusCode, requestId, url, retryCount):

Error class reference

ValidationError can also be thrown synchronously — as a rejected promise, before any network call is made — for malformed input like bad ISO codes, out-of-range coordinates, or invalid limits.

Retries & Timeouts

GET requests are retried automatically on transient network errors, 429, and 5xx responses — never on 401/403/404/400-class responses, and never on a caller-initiated AbortSignal cancellation. Defaults: 2 retries, full-jitter exponential backoff (200ms base, 2000ms cap). Any Retry-After response header takes precedence over the computed delay.
timeout applies per attempt, not as a total budget — worst-case latency for a call is roughly timeout × attempts + sum(backoff delays).
Override per call:

Response Metadata

meta.dataVersion is the SDK-side way to read the same value documented on Get Data Version. When the response includes the data-version header, no extra request is needed.
csc.getLastResponseMeta() returns the metadata from the most recent successful request on that client instance — useful for surfacing usage in a UI without an extra call.

Browser Usage

A key embedded in browser JavaScript is public — visible in your bundle and every outgoing request. Never use an unrestricted/server key here. Instead, create a key in your CSC dashboard that’s restricted to specific allowed origins (your site’s domain(s)); requests from any other origin are rejected server-side. The SDK adds no protection on top of this — origin restriction is an account/dashboard setting, not something the client enforces.

Node.js and Next.js

Use the SDK directly in any Node.js 18+ script or backend service. In Next.js, keep it server-side — in a Route Handler, Server Component, or Server Action — so your API key never reaches the client bundle.

Bundle Size

~6.9KB gzipped (ESM build), against a 20KB budget enforced in CI — zero runtime dependencies.

Migrating from the Local Packages

If you’re already using @countrystatecity/countries and want live, quota-aware data instead of the weekly-updated bundled snapshot:

Why migrate

  • You need data more current than the weekly-updated local snapshot.
  • You need search or usage — neither has a local-package equivalent.
  • You’re already calling the live API elsewhere and want one typed client instead of hand-rolled fetch calls.

Why not to migrate

If you don’t need any of the above, staying on the local package is usually the better choice — it’s free, has no network latency, and isn’t subject to plan quotas.

What changes

  • An API key is required. The local package needs none.
  • Every call is async and can fail with a network-shaped error (RateLimitError, NetworkError, TimeoutError, and others) in addition to the local package’s simpler failure modes.
  • Results are quota-limited per your plan (RateLimitError), not just rate-limited by your own code.
  • The shape is { data, meta }, not the bare array/object the local package returns — destructure data at the call site.
You don’t have to choose one. Keep @countrystatecity/countries for offline/bundled lookups and add @countrystatecity/sdk only for what it uniquely provides — search, usage, and guaranteed-current data. They don’t conflict.

TypeScript Types

Source

GitHub Repository

View the SDK source, open issues, or contribute.

npm Package

View published versions and changelog.