{"_id":"@countrystatecity/sdk","_rev":"2-6351f81791bcff89bfa6ed6717d8ccb5","name":"@countrystatecity/sdk","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@countrystatecity/sdk","version":"0.1.0","keywords":["country-state-city","sdk","api-client","country","state","city","geography","iso","typescript","rest-api"],"author":{"name":"dr5hn"},"license":"MIT","_id":"@countrystatecity/sdk@0.1.0","maintainers":[{"name":"dr5hn","email":"gadadarshan@gmail.com"}],"homepage":"https://github.com/dr5hn/countrystatecity-npm/tree/main/packages/sdk#readme","bugs":{"url":"https://github.com/dr5hn/countrystatecity-npm/issues"},"dist":{"shasum":"45c74ad28ae84f133795022bc9b62c81cc09eff8","tarball":"https://registry.npmjs.org/@countrystatecity/sdk/-/sdk-0.1.0.tgz","fileCount":9,"integrity":"sha512-qu14ohZslGbh96/HPdUP+dhH1vNtkYJfidWq9SuYgvsDsxUtdOoU77cQaj4mYtIdDE6hh6hKZieMMZBKNZcYyg==","signatures":[{"sig":"MEUCIQCD6RtvfVOGYbySiSOu7Su0VCMfHgvSEoZ9KzGT6sGlqAIgbT+34WSlXF86C4kbK+XhJpAcqbMkjMqfd0u1lpGJZ3I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":258852},"main":"./dist/index.cjs","type":"module","_from":"file:countrystatecity-sdk-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"scripts":{"dev":"tsup --watch","lint":"eslint src --ext .ts","size":"node scripts/check-size.cjs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"dr5hn","email":"gadadarshan@gmail.com"},"_resolved":"/tmp/2667af8eb222396c512ccc7928293372/countrystatecity-sdk-0.1.0.tgz","_integrity":"sha512-qu14ohZslGbh96/HPdUP+dhH1vNtkYJfidWq9SuYgvsDsxUtdOoU77cQaj4mYtIdDE6hh6hKZieMMZBKNZcYyg==","repository":{"url":"git+https://github.com/dr5hn/countrystatecity-npm.git","type":"git"},"_npmVersion":"10.8.2","description":"Official JS/TS client SDK for the CountryStateCity REST API — countries, states, cities, regions, currencies, phone codes, timezones, search, and usage","directories":{"test":"tests","example":"examples"},"_nodeVersion":"20.20.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^4.0.16","typescript":"^5.9.3","@types/node":"^20.19.19"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.0_1787560748130_0.48589454354497863","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@countrystatecity/sdk","version":"0.2.0","description":"Official JS/TS client SDK for the CountryStateCity REST API — countries, states, cities, regions, currencies, phone codes, timezones, search, and usage","keywords":["country-state-city","sdk","api-client","country","state","city","geography","iso","typescript","rest-api"],"author":{"name":"dr5hn"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/dr5hn/countrystatecity-npm.git"},"homepage":"https://github.com/dr5hn/countrystatecity-npm/tree/main/packages/sdk#readme","bugs":{"url":"https://github.com/dr5hn/countrystatecity-npm/issues"},"type":"module","engines":{"node":">=18"},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"devDependencies":{"@types/node":"^20.19.19","tsup":"^8.5.1","typescript":"^5.9.3","vitest":"^4.0.16"},"publishConfig":{"access":"public"},"directories":{"example":"examples","test":"tests"},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","lint":"eslint src --ext .ts","typecheck":"tsc --noEmit","size":"node scripts/check-size.cjs"},"_id":"@countrystatecity/sdk@0.2.0","_integrity":"sha512-qBfV8zfJ6c/GvdJVrUdqQ2bgw9vhJKZ0Ik6vq61a/ly22pIYKjAa4gCgFC4fqbb/3HNcIPDtKzNGT4fpIzhvig==","_resolved":"/tmp/dff547df940aa0f2ee1f1104cd90dc3d/countrystatecity-sdk-0.2.0.tgz","_from":"file:countrystatecity-sdk-0.2.0.tgz","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-qBfV8zfJ6c/GvdJVrUdqQ2bgw9vhJKZ0Ik6vq61a/ly22pIYKjAa4gCgFC4fqbb/3HNcIPDtKzNGT4fpIzhvig==","shasum":"1eb11934233d265927526a3db9fba6fccae8e770","tarball":"https://registry.npmjs.org/@countrystatecity/sdk/-/sdk-0.2.0.tgz","fileCount":9,"unpackedSize":362254,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCRQl3kwLDgnl7pE6Id8kAnkWRak82LbqDG3kYsds8SPwIhAM788ipqAy7SpMh7taYBTSpf+BGUp9o+BAWI3a8O60fA"}]},"_npmUser":{"name":"dr5hn","email":"gadadarshan@gmail.com"},"maintainers":[{"name":"dr5hn","email":"gadadarshan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.2.0_1788009538575_0.8551190681322214"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-24T08:39:07.848Z","modified":"2026-08-29T13:18:58.843Z","0.1.0":"2026-08-24T08:39:08.274Z","0.2.0":"2026-08-29T13:18:58.716Z"},"bugs":{"url":"https://github.com/dr5hn/countrystatecity-npm/issues"},"author":{"name":"dr5hn"},"license":"MIT","homepage":"https://github.com/dr5hn/countrystatecity-npm/tree/main/packages/sdk#readme","keywords":["country-state-city","sdk","api-client","country","state","city","geography","iso","typescript","rest-api"],"repository":{"type":"git","url":"git+https://github.com/dr5hn/countrystatecity-npm.git"},"description":"Official JS/TS client SDK for the CountryStateCity REST API — countries, states, cities, regions, currencies, phone codes, timezones, search, and usage","maintainers":[{"name":"dr5hn","email":"gadadarshan@gmail.com"}],"readme":"# @countrystatecity/sdk\n\n[![npm](https://img.shields.io/npm/v/@countrystatecity/sdk)](https://www.npmjs.com/package/@countrystatecity/sdk)\n[![CI](https://github.com/dr5hn/countrystatecity-npm/workflows/Pipeline/badge.svg)](https://github.com/dr5hn/countrystatecity-npm/actions/workflows/ci.yml)\n\nOfficial JS/TS client for the live [CountryStateCity REST API](https://countrystatecity.in) — countries, states, cities, regions, currencies, phone codes, timezones, fuzzy search, and account usage. Zero runtime dependencies, dual ESM/CJS builds, full TypeScript types.\n\n**Environment:** 🌐 **Node.js 18+ / Browser**\n\nNeed offline/bundled data instead of a live API call? See [`@countrystatecity/countries`](https://www.npmjs.com/package/@countrystatecity/countries) (Node) or [`@countrystatecity/countries-browser`](https://www.npmjs.com/package/@countrystatecity/countries-browser) (browser) and the [migration guide](./MIGRATION.md) below.\n\n## ✨ Features\n\n- 📦 Typed resource groups: `countries`, `states`, `cities`, `regions`, `currencies`, `iso`, `phone`, `timezones`, `search`, `usage`\n- 🪶 No runtime dependencies — built on native `fetch`, `URL`/`URLSearchParams`, `AbortController`\n- 🛡️ Structured errors (`AuthenticationError`, `ValidationError`, `FeatureRestrictedError`, `RateLimitError`, `NotFoundError`, `NetworkError`, `TimeoutError`) instead of generic exceptions\n- 🔁 Automatic retries with jittered backoff for transient failures, respecting `Retry-After`\n- ✅ Client-side input validation (ISO codes, coordinates, limits) before any network call\n- 📊 Response metadata (request id, rate-limit usage, data version, cache status) exposed alongside — never merged into — the entity data\n\n## 📦 Installation\n\n```bash\nnpm install @countrystatecity/sdk\n```\n\nYou'll need an API key — get one at [countrystatecity.in](https://countrystatecity.in).\n\n## 🚀 Quick Start\n\n```typescript\nimport { createCSCClient } from '@countrystatecity/sdk';\n\nconst csc = createCSCClient({ apiKey: process.env.CSC_API_KEY! });\n\nconst { data: countries } = await csc.countries.list();\nconst { data: states } = await csc.states.list({ country: 'IN' });\nconst { data: cities } = await csc.cities.list({ country: 'IN', state: 'MH' });\n\nconst { data: matches } = await csc.search.fuzzy({\n  query: 'Mumbay',\n  type: 'city',\n  country: 'IN',\n  limit: 10,\n});\n```\n\nEvery resource method returns `{ data, meta }` — `data` is exactly the API's entity payload, unwrapped and untouched; `meta` carries request/rate-limit/cache metadata alongside it. See [Response metadata](#-response-metadata).\n\n## ⚙️ Configuration\n\n```typescript\nconst csc = createCSCClient({\n  apiKey: 'your-api-key',        // required — never read from env or persisted by the SDK\n  baseUrl: 'https://api.countrystatecity.in/v1', // default\n  timeout: 10_000,                // ms, per request attempt, default 10000\n  fetch: myCustomFetch,           // optional custom fetch implementation\n  headers: { 'X-Trace-Id': 'x' }, // extra headers merged into every request\n  retry: { retries: 2, baseDelayMs: 200, maxDelayMs: 2000 }, // or `false` to disable\n  userAgent: 'my-app/1.0',        // overrides the default countrystatecity-sdk-js/<version>\n});\n```\n\nThe SDK never reads `apiKey` from an environment variable and never writes it to disk — pass it explicitly at construction time.\n\n## 📖 Resource reference\n\nEvery `list`/`get`/etc. method also accepts a trailing `{ signal?, timeout?, headers? }` for per-call overrides (see [Retries & timeouts](#-retries--timeouts)).\n\n| Resource | Methods |\n|---|---|\n| `csc.countries` | `list({ limit?, offset?, fields?, sort?, locale?, includeTranslations? })`, `get(iso2, { locale?, includeTranslations? })` |\n| `csc.states` | `list({ country?, limit?, offset?, fields?, sort?, locale?, includeTranslations? })`, `get(country, stateCode, { locale?, includeTranslations? })` |\n| `csc.cities` | `list({ country, state?, kind?, limit?, offset?, fields?, sort?, locale?, includeTranslations? })` — `country` is required, unlike `countries`/`states`; `get(country, stateCode, cityId)` always throws `ValidationError`, see note below |\n| `csc.regions` | `list()`, `get(id)`, `subregions(regionId)`, `getSubregion(id)`, `countries(subregionId)` — each accepts localization options |\n| `csc.currencies` | `list()`, `get(code)`, `byCountry(iso2)` |\n| `csc.iso` | `lookup({ iso2? \\| iso3? \\| numeric? })` |\n| `csc.phone` | `list()`, `get(iso2)`, `byDialCode(dialCode)` |\n| `csc.timezones` | `list()`, `byCountry(iso2)`, `convert({ time, from, to })` |\n| `csc.search` | `fuzzy({ query, type?, country?, limit?, threshold?, locale?, includeTranslations? })` — typo-tolerant and translated-name search; results include `match_score`/`matched_alias` and a client-injected `type` field |\n| `csc.search` | `autocomplete({ query, type?, country?, state?, limit?, locale?, includeTranslations? })` — type-ahead search with a ready display `label`; `matched_field` can be `name`, `native`, or `translation`; Professional or Business plan |\n| `csc.search` | `nearby({ lat, lng, type?, kind?, country?, state?, minPopulation?, radius?, limit?, locale?, includeTranslations? })` — localized nearby places, nearest first; radius 1–500 km; Professional or Business plan. [Compare plans](https://countrystatecity.in/pricing?source=sdk_docs&campaign=nearby_search&package=sdk). |\n| `csc.usage` | `get()` — returns cached rate-limit usage from the last request when available, otherwise makes one lightweight request |\n| `csc.changes` | `list({ startDate?, placeType?, countryCode?, changeType?, limit?, nextPageToken? })` — cursor-paginated country, state, and city changes; Business plan. [Compare plans](https://countrystatecity.in/pricing?source=sdk_docs&campaign=data_change_feed&package=sdk). |\n\n`csc.cities.list()` requires `country` (the real API has no bare `GET /cities` route) — a `ValidationError` is thrown client-side if it's missing, same as `state` without `country`. `csc.cities.get()` always throws a `ValidationError` — the real API has no single-city-by-ID endpoint at all; fetch the containing `list({ country, state })` and find the city in the results instead.\n\n`fields`/`sort` (e.g. `fields: ['name', 'iso2']`, `sort: ['name:desc']`) map to the API's `?fields=`/`?sort=` (Supporter+ plan) — validated server-side, so an unknown field throws a `ValidationError`-mapped error from the response rather than client-side.\n\n`locale` adds `localized_name` and `matched_locale` while keeping the English `name` and stable `id`. The fallback order is exact locale, base language, native name, then English. `includeTranslations: true` adds the full translation JSON string only when needed. Geographic routes and fuzzy, autocomplete, and nearby search support both options for Professional and Business plans. [Compare plans](https://countrystatecity.in/pricing?source=sdk_docs&campaign=localized_place_data&package=sdk).\n\n`csc.changes.list()` returns `{ results, next_page_token }`. Pass `next_page_token` back as `nextPageToken` to continue the same fixed snapshot; changes published after page one appear in a new request, not halfway through the current one. Tokens expire after 24 hours and changes are retained for 90 days. `old_values` and `new_values` contain only caller-visible fields; updates include only changed fields. When `startDate` is too old, `ValidationError.details` includes the API's `earliestAvailableDate`.\n\n## 🛡️ Error handling\n\nAll errors extend `CSCError` (`message`, `statusCode`, `requestId`, `url`, `retryCount`):\n\n```typescript\nimport {\n  AuthenticationError,\n  ForbiddenError,\n  ValidationError,\n  FeatureRestrictedError,\n  RateLimitError,\n  NotFoundError,\n  NetworkError,\n  TimeoutError,\n} from '@countrystatecity/sdk';\n\ntry {\n  await csc.search.fuzzy({ query: 'Mumbai' });\n} catch (err) {\n  if (err instanceof ValidationError) {\n    console.error(`Bad input: ${err.field} — ${err.reason}`);\n  } else if (err instanceof AuthenticationError) {\n    console.error('Invalid or missing API key.');\n  } else if (err instanceof ForbiddenError) {\n    console.error('Request blocked by API-key domain or IP restrictions.');\n  } else if (err instanceof FeatureRestrictedError) {\n    console.error(`\"${err.feature}\" needs the ${err.requiredPlan} plan (you're on ${err.currentPlan}).`);\n    console.error(`Upgrade: ${err.upgradeUrl}`);\n  } else if (err instanceof RateLimitError) {\n    console.error(`Rate limited (${err.scope}). Retry after ${err.retryAfter}s.`);\n    console.error(`Upgrade: ${err.upgradeUrl}`);\n  } else if (err instanceof NotFoundError) {\n    console.error(`${err.resource} \"${err.identifier}\" not found.`);\n  } else if (err instanceof TimeoutError) {\n    console.error(`Timed out after ${err.timeoutMs}ms.`);\n  } else if (err instanceof NetworkError) {\n    console.error('Network or server error:', err.message);\n  }\n}\n```\n\n`ValidationError` is also thrown synchronously-as-a-rejection for malformed input (bad ISO codes, out-of-range coordinates, invalid limits) before any network call is made.\n\n## 🔁 Retries & timeouts\n\nGET 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), and any `Retry-After` response header takes precedence over the computed delay.\n\n```typescript\nconst csc = createCSCClient({ apiKey: 'k', retry: false }); // disable entirely\nconst csc2 = createCSCClient({ apiKey: 'k', retry: { retries: 5, baseDelayMs: 100, maxDelayMs: 5000 } });\n```\n\n`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:\n\n```typescript\nconst controller = new AbortController();\nsetTimeout(() => controller.abort(), 3000);\n\nawait csc.countries.list(undefined, { signal: controller.signal, timeout: 5000 });\n```\n\n## 📊 Response metadata\n\n```typescript\nconst { data, meta } = await csc.countries.list();\n\nmeta.requestId;        // string | undefined\nmeta.rateLimit;         // { dailyUsed, dailyLimit, monthlyUsed, monthlyLimit } | undefined\nmeta.dataVersion;        // string | undefined\nmeta.cache;               // 'HIT' | 'MISS' | 'DYNAMIC' | undefined\nmeta.retryCount;           // number of retries this call needed\n```\n\n`csc.getLastResponseMeta()` returns the metadata from the most recent successful request on that client instance, useful for surfacing usage without an extra call.\n\n## 🌐 Browser usage\n\n```typescript\nimport { createCSCClient } from '@countrystatecity/sdk';\n\nconst csc = createCSCClient({ apiKey: PUBLISHABLE_RESTRICTED_KEY });\nconst { data } = await csc.countries.list();\n```\n\n**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 will be rejected server-side. The SDK does not add any additional protection on top of this — origin restriction is an account/dashboard setting, not a client-side one.\n\n## 🟢 Node.js usage\n\nSee [`examples/node`](./examples/node) for runnable scripts covering the happy path and full error handling.\n\n## ▲ Next.js usage\n\nKeep the SDK **server-side** — in a Route Handler, Server Component, or Server Action — so your API key never reaches the client bundle. See [`examples/nextjs`](./examples/nextjs) for an annotated Route Handler.\n\n## 🔧 TypeScript types\n\n```typescript\nimport type {\n  CSCClientOptions,\n  CSCResponse,\n  CSCResponseMeta,\n  ICountry,\n  IState,\n  ICity,\n  IRegion,\n  ISubregion,\n  ICurrency,\n  IPhonecode,\n  ITimezone,\n  IConvertedTime,\n  ISearchResult,\n  IUsageSnapshot,\n} from '@countrystatecity/sdk';\n```\n\n## 🔀 Migrating from a local data package?\n\nIf you're calling `@countrystatecity/countries` and want live/quota-aware data instead of the bundled snapshot, see [MIGRATION.md](./MIGRATION.md).\n\n## 📄 License\n\nMIT\n","readmeFilename":"README.md"}