TypeScript 97.5%
SQL 1.4%
Python 0.8%
1import type { Metadata } from "next";2import { COUNTRIES } from "@fetcha/core";3import { DocPage } from "@/components/docs/doc-page";4import { A, Code, H2, H3, Li, P, Strong, Table, TBody, Td, Th, THead, Tr, Ul } from "@/components/docs/prose";5import { Callout } from "@/components/docs/callout";6import { CodeTabs } from "@/components/docs/code-tabs";7import { ParamTable } from "@/components/docs/param-table";8import { ResponseExample } from "@/components/docs/response-example";9import { fetchTabs } from "@/components/docs/snippets";1011export const metadata: Metadata = {12 title: "Geolocation",13 description: "Target a country, region and city for the exit IP: accepted values, normalisation rules and behaviour when a location is unavailable.",14};1516const CA_REGIONS: Array<[string, string, string]> = [17 ["QC", "Quebec", "quebec"],18 ["ON", "Ontario", "ontario"],19 ["BC", "British Columbia", "british_columbia"],20 ["AB", "Alberta", "alberta"],21 ["MB", "Manitoba", "manitoba"],22 ["SK", "Saskatchewan", "saskatchewan"],23 ["NS", "Nova Scotia", "nova_scotia"],24 ["NB", "New Brunswick", "new_brunswick"],25 ["NL", "Newfoundland and Labrador", "newfoundland_and_labrador"],26 ["PE / PEI", "Prince Edward Island", "prince_edward_island"],27 ["YT", "Yukon", "yukon"],28 ["NT", "Northwest Territories", "northwest_territories"],29 ["NU", "Nunavut", "nunavut"],30];3132const US_SAMPLE: Array<[string, string]> = [33 ["CA", "california"],34 ["NY", "new_york"],35 ["TX", "texas"],36 ["FL", "florida"],37 ["WA", "washington"],38 ["IL", "illinois"],39 ["MA", "massachusetts"],40 ["NJ", "new_jersey"],41 ["NC", "north_carolina"],42 ["DC", "district_of_columbia"],43];4445export default function GeolocationPage() {46 const countries = Object.entries(COUNTRIES);47 return (48 <DocPage path="/docs/geolocation" eyebrow="Core API" title="Geolocation" description="Choose where your request appears to come from. Country is the primary control; region and city refine it where the network has coverage." status="Stable">49 <H2>Fields</H2>50 <P>51 The three geography fields are shared by <Code>POST /v1/fetch</Code> and <Code>POST /v1/sessions</Code>. All are optional. When none is set, the network picks any exit and{" "}52 <Code>metadata.country</Code> is <Code>null</Code>.53 </P>54 <ParamTable55 showDefault={false}56 rows={[57 { name: "country", type: "string", constraints: "exactly 2 characters", description: <>ISO 3166-1 alpha-2 code. Case-insensitive; normalised to upper case (<code>{`"ca"`}</code> → <code>{`"CA"`}</code>). Any other length fails validation with <code>INVALID_REQUEST</code>.</> },58 { name: "region", type: "string", constraints: "≤ 64 characters", description: <>State, province or territory. For <code>US</code> and <code>CA</code>, 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.</> },59 { name: "city", type: "string", constraints: "≤ 128 characters", description: <>City name, slugified: accents removed, lower-cased, non-alphanumeric runs replaced by <code>_</code>. <code>{`"Québec"`}</code> → <code>quebec</code>, <code>{`"Saint-Jean-sur-Richelieu"`}</code> → <code>saint_jean_sur_richelieu</code>.</> },60 ]}61 />6263 <H2>Example</H2>64 <P>Fetch through a residential exit in Québec City, Canada.</P>65 <CodeTabs66 tabs={fetchTabs(67 { url: "https://www.example.ca/", country: "CA", region: "QC", city: "Quebec" },68 {69 javascript: `console.log(data.metadata.country); // "CA"`,70 python: `print(data["metadata"]["country"]) # "CA"`,71 },72 )}73 />74 <ResponseExample75 status={200}76 title="200 OK · metadata excerpt"77 body={{ metadata: { network: "residential", country: "CA", attempts: 1, duration_ms: 1290, bytes: 61233, session: null, cached: false } }}78 />7980 <H2>Normalisation</H2>81 <P>82 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 recognised83 aliases for Canada and a sample for the United States; every US state, plus <Code>DC</Code>, is supported.84 </P>85 <H3>Canadian provinces and territories</H3>86 <Table dense>87 <THead>88 <Tr>89 <Th>Code</Th>90 <Th>Name</Th>91 <Th>Normalised value</Th>92 </Tr>93 </THead>94 <TBody>95 {CA_REGIONS.map(([code, name, slug]) => (96 <Tr key={slug}>97 <Td mono>{code}</Td>98 <Td>{name}</Td>99 <Td mono>{slug}</Td>100 </Tr>101 ))}102 </TBody>103 </Table>104 <H3>US states (sample)</H3>105 <Table dense>106 <THead>107 <Tr>108 <Th>Code</Th>109 <Th>Normalised value</Th>110 </Tr>111 </THead>112 <TBody>113 {US_SAMPLE.map(([code, slug]) => (114 <Tr key={code}>115 <Td mono>{code}</Td>116 <Td mono>{slug}</Td>117 </Tr>118 ))}119 </TBody>120 </Table>121 <P>122 Full names are accepted for both countries (<Code>{`"Ontario"`}</Code>, <Code>{`"British Columbia"`}</Code>, <Code>{`"Québec"`}</Code>). Only the two-letter postal code is accepted for US states; full US state123 names fall through to plain slugification, which still yields the same slug for single-word states (<Code>{`"texas"`}</Code>) but not for multi-word ones.124 </P>125126 <H2>Countries</H2>127 <P>128 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 confirmed129 with a test request.130 </P>131 <div className="my-5 grid grid-cols-2 gap-x-6 gap-y-1 rounded-lg border border-border p-4 text-[13px] sm:grid-cols-3">132 {countries.map(([code, name]) => (133 <div key={code} className="flex items-baseline gap-2">134 <code className="font-mono text-[12px] text-fg">{code}</code>135 <span className="truncate text-fg-muted">{name}</span>136 </div>137 ))}138 </div>139140 <H2>When a location is unavailable</H2>141 <Ul>142 <Li>143 <Strong>No route can serve the geography.</Strong> If none of the eligible routes supports the requested country, the request fails before any attempt: <Code>503 PROVIDER_UNAVAILABLE</Code>{" "}144 in <Code>auto</Code> mode, <Code>400 NETWORK_UNAVAILABLE</Code> with an explicit class. For sessions, creation fails with <Code>NETWORK_UNAVAILABLE</Code>.145 </Li>146 <Li>147 <Strong>Region or city has thin coverage.</Strong> Country is the level the routing engine guarantees when it selects a route. Region and city are forwarded to the network as targeting148 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 use149 case, confirm it with a request to a geo-echo endpoint of your choice.150 </Li>151 <Li>152 <Strong>Sessions inherit geography.</Strong> When a fetch carries a <Code>session</Code> and omits <Code>country</Code>, the session's country is used. See{" "}153 <A href="/docs/sessions">Sessions</A>.154 </Li>155 </Ul>156 <Callout variant="info" title="metadata.country echoes your request">157 <Code>metadata.country</Code> reports the country that was targeted for the final attempt (yours or the session's), not a measured location. It is <Code>null</Code> when you did not ask158 for one.159 </Callout>160 </DocPage>161 );162}163