import type { Metadata } from "next"; import { COUNTRIES } from "@fetcha/core"; import { DocPage } from "@/components/docs/doc-page"; import { A, Code, H2, H3, Li, P, Strong, Table, TBody, Td, Th, THead, Tr, Ul } from "@/components/docs/prose"; import { Callout } from "@/components/docs/callout"; import { CodeTabs } from "@/components/docs/code-tabs"; import { ParamTable } from "@/components/docs/param-table"; import { ResponseExample } from "@/components/docs/response-example"; import { fetchTabs } from "@/components/docs/snippets"; export const metadata: Metadata = { title: "Geolocation", description: "Target a country, region and city for the exit IP: accepted values, normalisation rules and behaviour when a location is unavailable.", }; const CA_REGIONS: Array<[string, string, string]> = [ ["QC", "Quebec", "quebec"], ["ON", "Ontario", "ontario"], ["BC", "British Columbia", "british_columbia"], ["AB", "Alberta", "alberta"], ["MB", "Manitoba", "manitoba"], ["SK", "Saskatchewan", "saskatchewan"], ["NS", "Nova Scotia", "nova_scotia"], ["NB", "New Brunswick", "new_brunswick"], ["NL", "Newfoundland and Labrador", "newfoundland_and_labrador"], ["PE / PEI", "Prince Edward Island", "prince_edward_island"], ["YT", "Yukon", "yukon"], ["NT", "Northwest Territories", "northwest_territories"], ["NU", "Nunavut", "nunavut"], ]; const US_SAMPLE: Array<[string, string]> = [ ["CA", "california"], ["NY", "new_york"], ["TX", "texas"], ["FL", "florida"], ["WA", "washington"], ["IL", "illinois"], ["MA", "massachusetts"], ["NJ", "new_jersey"], ["NC", "north_carolina"], ["DC", "district_of_columbia"], ]; export default function GeolocationPage() { const countries = Object.entries(COUNTRIES); return (

Fields

The three geography fields are shared by POST /v1/fetch and POST /v1/sessions. All are optional. When none is set, the network picks any exit and{" "} metadata.country is null.

ISO 3166-1 alpha-2 code. Case-insensitive; normalised to upper case ({`"ca"`} → {`"CA"`}). Any other length fails validation with INVALID_REQUEST. }, { name: "region", type: "string", constraints: "≤ 64 characters", description: <>State, province or territory. For US and CA, two-letter postal codes and full names are recognised and normalised to a canonical slug. For every other country the value is slugified as-is. }, { name: "city", type: "string", constraints: "≤ 128 characters", description: <>City name, slugified: accents removed, lower-cased, non-alphanumeric runs replaced by _. {`"Québec"`} → quebec, {`"Saint-Jean-sur-Richelieu"`} → saint_jean_sur_richelieu. }, ]} />

Example

Fetch through a residential exit in Québec City, Canada.

Normalisation

Fetcha converts what you send into a canonical internal form before handing it to the network, so the same location can be written several ways. The tables below show the recognised aliases for Canada and a sample for the United States; every US state, plus DC, is supported.

Canadian provinces and territories

{CA_REGIONS.map(([code, name, slug]) => ( ))}
Code Name Normalised value
{code} {name} {slug}

US states (sample)

{US_SAMPLE.map(([code, slug]) => ( ))}
Code Normalised value
{code} {slug}

Full names are accepted for both countries ({`"Ontario"`}, {`"British Columbia"`}, {`"Québec"`}). Only the two-letter postal code is accepted for US states; full US state names fall through to plain slugification, which still yields the same slug for single-word states ({`"texas"`}) but not for multi-word ones.

Countries

The API accepts any two-letter code. The following {countries.length} countries are the ones surfaced in the dashboard pickers; coverage of others depends on the network and is best confirmed with a test request.

{countries.map(([code, name]) => (
{code} {name}
))}

When a location is unavailable

  • No route can serve the geography. If none of the eligible routes supports the requested country, the request fails before any attempt: 503 PROVIDER_UNAVAILABLE{" "} in auto mode, 400 NETWORK_UNAVAILABLE with an explicit class. For sessions, creation fails with NETWORK_UNAVAILABLE.
  • Region or city has thin coverage. Country is the level the routing engine guarantees when it selects a route. Region and city are forwarded to the network as targeting hints; the network serves the closest exit it has. Fetcha does not currently verify the exit location against an independent geo-IP database, so if the exact city matters to your use case, confirm it with a request to a geo-echo endpoint of your choice.
  • Sessions inherit geography. When a fetch carries a session and omits country, the session's country is used. See{" "} Sessions.
metadata.country reports the country that was targeted for the final attempt (yours or the session's), not a measured location. It is null when you did not ask for one.
); }