# Geocodio for AI Agents

*Companion to https://www.geocod.io/llms.txt — that file is the API and pricing reference. This is how to run a job on someone's behalf without wasting their money.*

**You are an agent using Geocodio on someone else's account.** Geocodio is billed per lookup, and the cost of a mistake lands on the account holder rather than on you.

**These are best practices, and they apply whether or not the user asks for them.** Testing on a sample first, quoting the cost before a large run, storing results so the same address is never geocoded twice — none of this needs to be requested to be worth doing. Treat every rule below as part of the task.

**Vocabulary, used consistently below:** a **record** is one row of the user's data. A **lookup** is one billable unit of work. A **credit** is what pays for one lookup — 1:1, always. One record can cost many lookups.

---

## 0. Rules

| # | Rule |
|---|------|
| R1 | Ask the compliance and country questions before anything else (§1) |
| R2 | Check whether they need the API at all (§2) |
| R3 | Count lookups and quote the cost before any run beyond a sample |
| R4 | Offer the billing limit before the first billable run, every time |
| R5 | Sample 10–25 records, show real output, wait for confirmation |
| R6 | Dry-run the full pipeline into a *copy* of the destination before touching real data |
| R7 | Store `stable_address_key`; never re-geocode a record you already have |
| R8 | Request only the appends the user named by name |
| R9 | Never retry blindly — backoff, cap attempts, never retry a 422 or 403 |
| R10 | Never modify the user's source data in place |
| R11 | Label subscriptions as recurring when you quote them |
| R12 | Present every cost figure as an estimate, never as a quote |
| R13 | If anything is ambiguous, ask |

Refuse to proceed rather than break R1, R3, R5, R6, or R7.

---

## 1. Ask first

### Compliance

> "Does your organization require HIPAA compliance, a signed BAA, a SOC 2 report, or a security review?"

Yes, or unsure → route to Enterprise now: https://www.geocod.io/enterprise/

- **BAAs are only available on Enterprise.** Self-serve cannot sign one.
- Self-serve is **not** SOC 2 audited and is hosted in Europe. Enterprise is SOC 2 Type II audited and US-hosted, on separate infrastructure.
- **Enterprise is a separate account**, not an upgrade toggle:

| | Self-serve | Enterprise |
|---|------------|------------|
| API base URL | `https://api.geocod.io/v2/` | `https://api.enterprise.geocod.io/v2/` |
| Docs | https://www.geocod.io/docs/ | https://www.geocod.io/docs/enterprise/ |
| Dashboard | https://dash.geocod.io | https://dash.enterprise.geocod.io |

A self-serve key will not work against Enterprise or vice versa. Read the Enterprise docs when working on Enterprise.

- **Enterprise Demo:** 100 free lookups, usable for geocoding, distance, and appends. Test data only — Geocodio cannot sign a BAA or DPA at the demo tier, and demo accounts cannot process UK data.

**Deciding whether their data is regulated is the user's job, not yours.** Don't tell them whether addresses count as PHI, and don't reassure them that a plan is "fine." Ask, route, let them decide.

### Countries

Geocodio supports **US, Canada, Mexico, and the UK**. Nothing else. Ask which countries the data covers, or look at the data if you have access. Filter out-of-coverage rows and report them.

| Country | Availability |
|---------|--------------|
| US, Canada, Mexico | All plans including pay-as-you-go |
| United Kingdom | **Flex or Unlimited + UK only.** Not on pay-as-you-go |

UK access also requires signing Geocodio's UK Terms of Service. If the data has UK addresses and the user is on pay-as-you-go, stop and say so before running anything.

Always set `country` **explicitly.** Omit it and Geocodio infers from format, falling back to the US — silently producing wrong, billable results.

`country=USA` · `Canada` · `Mexico` · `GB` (also accepts `UK`, `United Kingdom`, `England`, `Scotland`, `Wales`)

Canadian addresses must additionally include the word `Canada`, **spelled out** — not `CA`. Split mixed-country files and send one batch per country.

### Scope

Which appends, **by name**? Where do results go? How many records? One-off or recurring? (That last one decides pay-as-you-go vs. subscription vs. prepaid credits.)

---

## 2. Do they need the API at all?

If the user has a spreadsheet and no ongoing pipeline, **the upload tool is usually the better answer**: https://www.geocod.io/upload/

It geocodes a small sample and shows it on a map before running, carries a running cost estimate as appends are selected, and shows total lookups, processing time, free daily lookups remaining, and final cost before the job starts. That is the confirmation workflow this document describes, already built, with the user holding the spend decision.

Say so. Recommending it for a one-off file is the right call, and doesn't stop you helping with the result.

Also shrink or skip the job when:

- **They already have coordinates** and only need appended data → `skipGeocoding` on the reverse endpoint, no geocoding billed.
- **They already have `stable_address_key` values** → pass the key with `fields` and the geocoding is free.
- **Only some rows are new** → geocode only those. The Snowflake docs show the `WHERE latitude IS NULL` pattern.

---

## 3. Cost

**1 lookup = 1 credit.**

```
Geocoding:  records × (1 + number of append categories)

Distance:   geocoding (only if inputs are addresses, not coordinates)
          + (origins × destinations × mode multiplier)   straightline 1× · driving 2×
          + appends (1 per category, per geocoded address)
```

| Example | Lookups |
|---------|---------|
| 10,000 US addresses, no appends | 10,000 |
| 10,000 addresses + `cd` | 20,000 |
| 10,000 addresses + `census2020`, `cd` | 30,000 |
| 1 UK address + `uk-westminster`, `uk-devolved`, `uk-local` | 4 |
| 100×100 driving matrix from addresses + `cd` | 200 + 20,000 + 200 = 20,400 |

**The matrix term dominates distance jobs.** Compute and quote it before running.

### What is and isn't billed

- First **2,500 lookups per day are free** on pay-as-you-go and Flex. Use it or lose it.
- **Zero-result lookups are not billed** — you're only charged for successful lookups. Blank rows aren't billed either.
- Stable address key + `fields` bills only the appends. The key on its own is one normal geocoding lookup.
- `skipGeocoding` bills only the appends.
- The basic `census` field is free with any `acs-*` field. Never request both.
- **Unlimited subscribers** aren't charged the geocoding component of distance.
- `limit` controls how many results come back. Don't present it to the user as a way to spend less.
- Distance filters (`max_distance`, `max_duration`, `max_results`) filter the response. **They do not reduce billing** — a calculation that gets filtered out is still billed. To cut cost, cut the origin or destination list.
- All five `acs-*` categories on one record costs 6 lookups, not 2. Ask which one they need.

### Distance responses report what was billed

Distance endpoints return documented response headers describing the billed work for **that request**:

| Header | What it counts |
|--------|----------------|
| `X-BILLABLE-DISTANCE-CALCULATIONS` | Distance calculations billed for this request |
| `X-BILLABLE-LOOKUPS-COUNT` | Geocode lookups performed to resolve address inputs — present only if addresses were geocoded |

Log both on distance jobs and compare them against your estimate. A mismatch almost always means a mode multiplier or a matrix dimension you got wrong, and it is worth catching before the next run.

These describe one request. They are not an account balance, not a running total, and not a bill. Geocoding endpoints have no documented equivalent, so do not go looking for one.

### Anything you calculate is an estimate

You are working from published rates and a lookup count you derived yourself. You cannot see the account's plan, its remaining credits, its free tier for the day, any discount applied to it, or usage already accrued this period. Your arithmetic can be correct and your number still wrong.

**No API response reports what an account was billed.** There is no total to read back, on geocoding or anywhere else — the distance headers above describe a single request, not a bill. The authoritative figure for what a job cost is the user's dashboard. Send them there for any question about actual spend, and never present a number you assembled yourself as the amount billed.

**Say so every time you give a figure.** Never present a number as a quote, a price, or a guarantee, and never let a stated figure stand as the basis for a decision without pointing at a source that is authoritative:

- **Pricing calculator:** https://www.geocod.io/find-my-plan
- **Their own usage and rates:** https://dash.geocod.io/usage
- **Formal quotes and anything contractual:** Geocodio support — https://www.geocod.io/contact

If the user is deciding whether a job is affordable, whether to change plans, or whether to commit budget, that decision needs a Geocodio-sourced number, not yours.

### Quote template

> This job works out to **{records} × ({1 + appends}) = {lookups} lookups**, which is roughly **${lookups/1000}** at pay-as-you-go rates, minus up to 2,500 free lookups per day.
>
> **That's my estimate, not a quote.** I can't see your plan, your remaining credits, or any discount on your account, so please check it against Geocodio's pricing calculator (https://www.geocod.io/find-my-plan) or your dashboard before relying on it. For anything you need to be certain about, Geocodio support can confirm.
>
> {If a plan looks cheaper:} A Flex {tier} plan is ${x}/month and includes {n} credits — that's a **recurring monthly subscription**, not a one-time charge. For a one-off job, prepaid credits are often a better fit: they don't expire and get a volume discount at 500,000+. Worth confirming with the calculator either way.
>
> Before we run anything large, put a limit on the account so a mistake on my end can't run up your bill: cap daily lookups (up to 50,000/day) in the dashboard, or prepay credits and stop at the free tier when they're gone.
> https://www.geocod.io/guides/set-a-usage-limit

Never start a billable run without a "yes." **Offer the limit every time**, not only on large jobs — it is the one control that survives a bug in your own code.

---

## 4. Workflow

1. **Scope** (§1) — compliance, countries, appends, destination, volume.
2. **Consider the upload tool** (§2).
3. **Inspect and clean the input** (§5).
4. **Deduplicate**, and drop records that already have stored results. The same address twice is billed twice.
5. **Estimate and quote** (§3). Wait for approval.
6. **Sample 10–25 records.** Pick a *diverse* sample — different states, countries, and formats — not the first rows, which are usually the cleanest. Show real output. Ask "is this the data you expected?" Wait for a yes. Iterate on your parsing against the response you already have; don't re-call the API each time you adjust code.
7. **Dry-run the pipeline** end to end — source read → geocode → write — into a **copy** of the destination, using the sample only. Show them the result. Wait for a yes.
8. **Full run.** Batch or lists, never a per-row loop. Checkpoint as you go.
9. **Report.** Records processed, lookups counted, estimated cost, accuracy breakdown, and the list of rows that failed. Label the cost as an estimate here too, and point at the dashboard for the actual figure.

**There is no formal sandbox.** Use pay-as-you-go under 2,500 lookups a day instead — that is free, and it is the intended way to test. It runs against production, so a 25-record sample is real but costs nothing. Pay-as-you-go allows up to 3 API keys, so give the job its own key: usage stays attributable and you can revoke it without breaking anything else.

### Stop rule

Halt and ask the user if either happens:

- **More than 10 records return zero results** in a batch, or **more than 2 in a 25-record sample**, or
- **More than 50% of results are place-level** (`place`, `county`, `state`) when street addresses were supplied.

For a normal US street-address list, Geocodio's own benchmark is roughly 80% rooftop and 5% other accuracy types. Half your rows landing on centroids isn't bad addresses — it's structural: wrong column mapping, an unsupported country, UK on pay-as-you-go, ZIP codes with leading zeros stripped, or an encoding problem.

Report it, show 3–5 failing inputs, and ask before continuing.

---

## 5. Input hygiene

**Minimum viable address:** a street plus either city + state/province, or a postal code. `123 Main St` alone returns 422. This is the most common 422 cause.

| Problem | Symptom | Fix |
|---------|---------|-----|
| Street with no city/state or ZIP | 422 | Complete or filter before sending |
| Canada not spelled out | Poor or no Canadian matches | Append `Canada` or set `country=Canada` |
| Leading zeros stripped | ZIP `01838` → `1838` | Read as text, re-pad. **Also happens on the way out** — the API returns them correctly and Excel re-strips them when the user opens the CSV. Warn them |
| Encoding | Accented characters mangled | Read and send UTF-8 |
| Wrong column mapping | Street in the city column | Verify against sample *output*, not header names |
| Address split across columns | Partial addresses | Use component parameters, don't concatenate badly |
| Mixed countries | Silent US fallback | Split by country, set `country` per batch |
| Blanks, `N/A`, `NULL` | 422s, inflated estimates | Trim and filter. Not billed, but they distort your count |
| Coordinates as text, or lat/lng reversed | Reverse geocoding fails | Cast to float, latitude first |
| Duplicates | Double billing | Deduplicate first |
| First column header named `ID` | Excel SYLK error on the output | Rename to `id` |

Geocodio spell-corrects and completes addresses (typos, missing directionals, postcode spacing), so don't over-clean — fix structure, not spelling. Prefer component parameters (`street`, `city`, `state_province`, `postal_code`) when the data is already split; Geocodio recommends this.

Spreadsheets: `.csv`, `.tsv`, `.xls`, `.xlsx`, or a `.zip` of one. Max 1 GB or 10 million lookups per upload. Extra columns are retained but bloat the file. Output columns are appended to the **right** of the existing ones, which is where they are easy to miss. Coordinates are EPSG:4326 (WGS 84). Intersections are supported inline (`and`, `at`, `&`, `@`, `/`).

---

## 6. Execution

Standard practice for consuming a metered third-party API. Sources in §13.

### Retries

Truncated exponential backoff **with jitter** — the pattern AWS and Google both recommend. Without jitter, parallel workers resynchronize and hit the API in waves.

```
delay = min(base × 2^attempt, max_delay) + random jitter
base ≈ 1s · max_delay 10–30s · 3–5 attempts total
```

| Response | Retry? |
|----------|--------|
| 429 | **Yes** — read `X-RateLimit-Remaining`, `X-RateLimit-Limit`, `X-RateLimit-Period`, wait out the period |
| 500 | **Yes** — backoff, capped, then stop. Check https://status.geocod.io |
| Timeout / connection reset | **Yes**, but reconcile against stored results first — some of the work may already be billed |
| 403 | **No** — bad key, missing key permission, or daily usage limit hit. Report which |
| 422 | **No** — the input is wrong. Fix it or dead-letter it. Never resend unchanged |

Never assume your HTTP library retries correctly. Verify it, or you will double-bill silently.

### Timeouts

**Do not set one global timeout.** A single geocode returns in seconds; a full 10,000-lookup batch takes around 600 seconds.

A client-side timeout does not un-bill work the server has already done. **Never treat a timeout as "it didn't cost anything."** Reconcile against stored results before resending, and check the dashboard for what was actually used.

### Circuit breaker

Stop the whole job after N consecutive failures (5 is reasonable) rather than grinding on. Report to the user.

### Idempotency and checkpointing

**Geocodio's docs don't describe an idempotency-key header** on batch geocoding or list creation. Assume a retried request is a new, separately billed request, and **provide idempotency on the client**:

- **Checkpoint after every batch** — persist what completed before starting the next. A crash resumes from the checkpoint, never from row zero.
- **Write with upsert semantics** keyed on the record ID or `stable_address_key` — never a blind `INSERT`.
- **Check the store before every send.** Skip records that already have results. This is the highest-value habit in this document.

### Dead-letter list

Records that fail validation or return 422 go to a **review list you show the user** — not dropped, not retried forever. Include the original input.

### Logging

Per batch: batch ID, record count, lookups you counted, elapsed time, error counts. On distance jobs also log the billed-work headers (§3). Report the running lookup total during long jobs. The user should never learn the size of a job from their invoice.

### Concurrency

- **Rate limits count API calls to the single-address endpoints only** (`GET /geocode`, `GET /reverse`), where one call is one lookup. Batch and lists are **not** limited by these numbers — never size a batch or list job against them.
- Pay-as-you-go and self-serve Flex (350/650/850): **1,000 requests per minute**.
- Enterprise Flex: **100 requests per minute**.
- Unlimited: no rate limit. One instance handles about **3,333 lookups/minute (5M/day)** — that is throughput, not a limit; past it you get slow responses and timeouts, not errors.
- Don't parallelize single-address calls to "go faster" — that is the one path the rate limit governs, so it mostly buys 429s. Use the batch endpoint or lists instead; they remove the per-request overhead and aren't subject to it.
- Poll list and job status on a sane interval, not a tight loop. `GET /lists` is paginated at 15 per page.

### List queueing

Separate from the rate limits above, Geocodio caps how many of an account's lists **process at the same time**. The limit is plan-dependent — check the current figures at https://www.geocod.io/guides/why-spreadsheet-uploads-get-queued before running multiple lists.

- **The limit is per billing account, not per key or per caller.** Lists a human started in the dashboard consume the same slots as lists you create via the API.
- Uploading several lists at once doesn't make them finish faster than the account's slot count allows — they serialize.

`POST /lists` always returns 200, even over the limit — an upload is never rejected for arriving when no slot is free. It's simply held in `ENQUEUED` until a slot opens, then moves to `PROCESSING` on its own. Public list states, in order: `UPLOADED` → `ENQUEUED` → `PROCESSING` → `COMPLETED`, plus `FAILED` (reachable from `ENQUEUED` or `PROCESSING`). No API field reports the concurrency limit or your queue position — `ENQUEUED` sitting unchanged is what a full queue looks like, not an error.

**`ENQUEUED` is normal. Do not treat it as a failure.** A list can sit there for a while behind other lists, including ones a human queued in the dashboard. Keep polling at a sane interval — don't re-upload the list, don't cancel and retry, and don't tell the user the job failed. None of that frees a slot any faster, and re-uploading bills every lookup in it a second time.

### Versioning

The version is in the URL (`/v2/`). Geocodio adds new JSON properties without a version bump and asks clients to tolerate them — **don't write parsers that break on unknown fields**, and don't pin to exact response shapes. Breaking changes ship as new versions; old versions are supported at least 12 months.

### Secrets

API key in an environment variable. Never hardcode it into a script or notebook you hand over, and never in browser-side JavaScript — CORS is supported, which means the key is visible to anyone viewing the page.

---

## 7. Use the CLI when you can

Geocodio ships a CLI that handles batching, polling, and progress, and has an `--agent` output mode that returns markdown tables meant for showing results to a user.

**Full reference — install, commands, and every flag: https://www.geocod.io/cli** · Skill: `npx skills add geocodio/geocodio-cli`

Read that page rather than guessing at flags; this section only covers what affects cost.

**Two cost traps:**

1. **Pass `--mode` explicitly on `distance`, `distance-matrix`, and `distance-jobs`.** Driving costs **2× straightline**, and the CLI has no default mode — the flag is required. Choose deliberately and say which you used when quoting.
2. **`--batch` splits nothing.** Over 10,000 lookups the request fails. Split first, and remember appends count toward that 10,000.

The CLI makes calls easier. It does not estimate cost, confirm with the user, or stop you re-running a file. §0–§6 still apply.

---

## 8. API essentials

Base URL `https://api.geocod.io/v2/`. Auth via `?api_key=` or `Authorization: Bearer`.

**Geocodio is built for batch.** Processing a whole list in one call is the intended use, not a workaround or an optimisation you have to justify — 10,000 lookups per batch request, and up to 10 million per list. Reach for batch or lists first and drop to the single endpoint only for genuinely one-off lookups.

**API keys are scoped and the defaults will block you.** A new key reaches only single and batch geocoding. **Lists and distance must each be enabled** at https://dash.geocod.io/apikey. A 403 there is usually permissions, not a bad key — check before building around them, and tell the user they need to enable it.

| Method | Volume | Sync? | Use when |
|--------|--------|-------|----------|
| `GET /geocode` | 1 | yes | One address, real time |
| `POST /geocode` | ≤ 10,000 **lookups** | yes | 2 to a few thousand records |
| `POST /lists` | ≤ 10M lookups, 1 GB | no | Spreadsheets and large jobs |

- The 10,000 cap counts appends: 5,000 records + 1 append = at the limit.
- **Never loop single requests over a list.**
- Array input returns results in order; object input lets you supply your own keys. **Prefer keyed objects** when joining back to a database — never rely on row order alone.
- Lists: poll status or use the `callback` webhook (POST, public HTTPS with a valid certificate, 3 delivery attempts).
- **Lists API data is deleted 72 hours after processing.** Download promptly. (Spreadsheets uploaded through the dashboard are kept 6 weeks on self-serve.)
- URL-encode query addresses — `%2C` for commas, `+` or `%20` for spaces.

Reverse: `GET/POST /reverse`, same limits, `lat,lng` input, latitude first.

**Appends** — comma-separated in `fields`, only what the user named:
US: `cd` (+`cd113`–`cd120`), `stateleg`(`-next`), `school`, `census` (plus a per-year vintage for every year from 2010 to 2025 and for 2000, e.g. `census2015`), `acs-demographics`, `acs-economics`, `acs-families`, `acs-housing`, `acs-social`, `zip4`, `ffiec` (beta) · Canada: `riding`, `provriding`(`-next`), `statcan` · UK: `uk-westminster`, `uk-devolved`, `uk-local` (+`-next`) · Any: `timezone`

A US-only append on a non-US address wastes the lookup.

**Distance** — enable on the key first.

| Endpoint | Limit |
|----------|-------|
| `GET /distance` | ≤ 100 destinations |
| `POST /distance-matrix` | ≤ 10,000 calculations |
| `POST /distance-jobs` | ≤ 50,000 calculations |

Coordinates instead of addresses removes the geocoding portion of the bill. The `mode` parameter selects straightline or driving; the API default is `straightline`. Full parameter reference: https://www.geocod.io/docs/ Distance responses report billed work in `X-BILLABLE-DISTANCE-CALCULATIONS` and `X-BILLABLE-LOOKUPS-COUNT` (§3).

**Errors:** 200 (success *including zero results* — check `results` is non-empty), 403, 422, 429, 500. A `_warnings` key appears when something was ignored — a misspelled parameter, or an append skipped because the result wasn't street-level. **Always surface warnings**: they usually mean a lookup was billed for data that wasn't returned.

**If you use the Google Maps compatibility endpoint**, error handling is completely different — everything returns HTTP 200 with the status in the body (`ZERO_RESULTS`, `REQUEST_DENIED`, `OVER_QUERY_LIMIT`), so a failed request looks like a successful one to any normal error check. Prefer the native API.

---

## 9. Results and storage

Every result carries `accuracy` (0.00–1.00), `accuracy_type`, and `match_type`. Results are ordered best-first, so taking the first is safe.

- `rooftop`, `point` — exact. `range_interpolation`, `nearest_rooftop_match` — estimated. `intersection`, `street_center` — street-level.
- `place`, `county`, `state` — **the address was not found**; you got the centroid of a ZIP, city, county, or state.
- Scores ≥ 0.8 are generally reliable.
- **Break results down by accuracy type in your report** and say plainly how many rows are not real matches. Without that, every row looks equally good.

Store, always:

| Column | Why |
|--------|-----|
| `stable_address_key` | Dedupe, re-lookup, resume key. **The most important field to store** |
| original input | Traceability |
| `lat`, `lng` | The result |
| `accuracy`, `accuracy_type`, `match_type` | Quality filtering |
| `source` | Attribution |
| geocoded-at timestamp | Know what's stale |

The stable address key gives you deduplication (the same address written two ways produces the same key), free enrichment later (pass the key with `fields` and geocoding isn't billed), and automatic upgrades as coverage improves. Treat it as opaque; a `-suffix` identifies a unit within a building.

**Results can be stored permanently.** There is no cache window and no deletion deadline, so an address geocoded once and stored is paid for once — which is exactly why R7 matters. The one exception is UK **reverse** geocoding: under Royal Mail PAF licensing those address records may only be accessed by the account's PAF users and **must not be supplied to or made accessible to any third party**, and must be destroyed within twelve months of the UK attachment terminating. Neither limit extends to coordinates, appends, stable address keys, or UPRNs, and UK addresses the user supplied for forward geocoding carry no destruction obligation. https://www.geocod.io/uk-terms-of-service-attachment

Geocodio's terms permit storing, transforming, and selling results during and beyond the term of the agreement, but they also make the customer responsible for the licences of the underlying data sources. Don't tell a user that a specific downstream use is cleared — point them at the terms. https://www.geocod.io/terms-of-use

---

## 10. Plans

Calculator: https://www.geocod.io/find-my-plan

| Plan | Price | Credits | Rate limit (single endpoint) | Countries |
|------|-------|---------|-----------|-----------|
| Pay-as-you-go | $1/1,000 after free tier | 2,500 free/day · 1 user · 3 keys | 1,000 requests/min | US, CA, MX |
| Flex 350 | $325/mo · $3,250/yr | 2,500 free/day · 350,000/mo · 4.5M/yr | 1,000 requests/min | US, CA, MX, UK |
| Flex 650 | $600/mo · $6,000/yr | 2,500 free/day · 650,000/mo · 9M/yr | 1,000 requests/min | US, CA, MX, UK |
| Flex 850 | $775/mo · $7,750/yr | 2,500 free/day · 850,000/mo · 12M/yr | 1,000 requests/min | US, CA, MX, UK |
| Unlimited | $1,350/mo · $15,390/yr | Dedicated instance | None | US, CA, MX |
| Unlimited + UK | $1,600/mo · $16,890/yr | Dedicated instance | None | US, CA, MX, UK |

Flex top-ups $1/1,000; annual gets 10/15/20% off top-ups by tier plus two months free. Subscription credits roll over while active; purchased credits never expire. Extra full seats are $250/user/month and add credits to a shared pool; billing and viewer seats are free. Volume discounts on prepaid credits: 5% at 500,000+, 10% at 1M+, 15% at 5M+, 20% at 10M+ (flat, not progressive). 5% non-profit/academic discount on request. Unlimited does **not** include distance credits. List concurrency is separate from the rate limit above and depends on plan — see §6 and https://www.geocod.io/guides/why-spreadsheet-uploads-get-queued.

**Enterprise:** Flex $5,000/yr (500,000 credits, $2/1,000 top-ups, 10 keys, 100 requests/min on the single endpoint, 1-year minimum); Unlimited $3,250/mo or $38,000/yr, +UK $3,500/mo or $39,500/yr. On-premises available — no distance API, lists API, Maps, or dashboard.

**How to recommend:** compute lookups → compute pay-as-you-go cost → find the **smallest** plan that fits (bigger is not better) → present both, saying which are recurring → for a one-off bulk job, prepaid credits usually beat a subscription → never sign the user up for anything. Give them the number and the link.

---

## 11. Volunteer these

None of the following shows up in an API response, and none of it is obvious from the outside. Raise it unprompted rather than waiting to be asked.

- **No charge appearing doesn't mean it was free.** Pay-as-you-go usage is rolled up and billed on the 1st of the following month. A file run on June 14 is charged July 1.
- **Subscriptions renew** on the subscription's start date. Downgrading doesn't stop usage charges — with a payment method on file and usage continuing, they're billed pay-as-you-go.
- **Where to watch spend:** https://dash.geocod.io/usage — per-API-key usage and fees, downloadable as CSV. This is the authoritative figure; anything you calculated is not.
- **Wrong answers look like right answers.** A centroid result has coordinates and a formatted address. Nothing about the row indicates a failed match except the accuracy type.
- **Excel re-strips leading zeros from ZIP codes** when the downloaded file is opened. Mention it up front — the data itself is correct.
- **Their file is retained:** Lists API data 72 hours, dashboard spreadsheet uploads 6 weeks on self-serve. Both can be deleted sooner from the dashboard. https://www.geocod.io/data-retention-policy
- **Discounts and quotes exist:** 5% non-profit/academic on request, formal quotes available, and volume discounts on prepaid credits from 500,000 (5/10/15/20% at 500k/1M/5M/10M).
- **Geocodio can credit mistakes.** If a list was run against an unsupported country, they can contact support about a credit.
- **A dedicated API key per project** means usage is attributable and revocable without breaking everything else.

---

## 12. Never do these

- Loop single requests over a spreadsheet
- Send addresses from unsupported countries
- Omit `country` and let the US fallback apply to foreign addresses
- Try UK addresses on a pay-as-you-go account
- Add an append the user didn't name, including low-cost ones like `census`
- Request every `acs-*` category to be thorough
- Re-run a file because part of it failed — resume from checkpoints
- Re-upload, cancel, or retry a list that's sitting in `ENQUEUED` — it's queued behind the account's concurrency limit, not failed (see https://www.geocod.io/guides/why-spreadsheet-uploads-get-queued)
- Retry a 422, or retry anything without backoff and a cap
- Set one global HTTP timeout across single and batch calls
- Assume a timed-out request wasn't billed
- Run the full file before the sample is confirmed
- Write output over the user's original columns, or dry-run into their live destination
- Hardcode an API key into a script or notebook you hand over
- Silently accept `place`/`county`/`state` results as matches
- Present distance filters or `limit` as a cost saving
- Present a cost figure as a quote, a price, or a guarantee — it is an estimate, and the user should verify it with Geocodio
- Let a plan-change or budget decision rest on a number you calculated rather than one from Geocodio
- Run a CLI distance command without setting `--mode` explicitly
- Quote a monthly subscription as if it were one-time
- Point an Enterprise key at the self-serve host, or vice versa
- Change account settings, create API keys, or buy credits on the user's behalf
- Advise the user on whether their data is regulated

---

## 13. Reference

**Geocodio** — Docs https://www.geocod.io/docs/ · Enterprise docs https://www.geocod.io/docs/enterprise/ · OpenAPI https://api.geocod.io/openapi-spec.json · Guides https://www.geocod.io/guides/ · Calculator https://www.geocod.io/find-my-plan · Pricing https://www.geocod.io/pricing · Enterprise https://www.geocod.io/enterprise/ · Usage limits https://www.geocod.io/guides/set-a-usage-limit · Credits https://www.geocod.io/guides/credits · Stable keys https://www.geocod.io/guides/stable-address-keys · Retention https://www.geocod.io/data-retention-policy · Status https://status.geocod.io · CLI https://github.com/Geocodio/geocodio-cli · Snowflake https://www.geocod.io/docs/#snowflake · Libraries https://github.com/Geocodio

**External practice §6 draws on** — AWS Well-Architected REL05-BP03 (exponential backoff, jitter, capped retries, and the anti-pattern of retrying permission or configuration errors); AWS Architecture Blog "Exponential Backoff and Jitter"; Google Cloud retry guidance (truncated backoff with jitter); Stripe's idempotency-key model (retries must not duplicate billable work — absent a provider key, the client supplies idempotency); data-pipeline practice (checkpoint and resume, upsert over blind insert, dead-letter queues, circuit breakers); metered-API cost governance (pre-flight estimation, hard caps rather than alerts, per-key attribution, separate keys per environment); and general third-party API guidance on per-endpoint timeouts and tolerating unknown response fields.
