# Geoverio — complete API reference Generated from the live API specification at 2026-09-13. This file is written for automated consumers: everything needed to build a working integration is below, with no HTML to parse. ## Authentication Send the key as a bearer token on every request: ``` Authorization: Bearer gv_live_xxxxxxxxxxxxxxxxxxxxxxxx ``` There is no other auth scheme: no query-string keys, no signing, no OAuth. The same key works across every API listed here; which APIs it may call is determined by the account's plan. Get a key: sign up at https://www.geoverio.com/register (free tier, no card), then Dashboard -> API Keys at https://www.geoverio.com/dashboard/keys. ### Key handling rules - Never put a `gv_live_` key in browser JavaScript, a mobile app bundle, or any client the end user controls. Anything shipped to a client is readable by that client. - For browser features such as address autocomplete, proxy through a thin endpoint on your own backend that adds the `Authorization` header. - Read the key from an environment variable or secret store. Do not commit it. ## Errors Every error is a standard HTTP status code with a JSON body describing the cause. | Status | Meaning | What the caller should do | |---|---|---| | 401 | Missing or invalid API key | Fix the key or the `Authorization` header. Never retry — it will keep failing. | | 403 | Key not permitted for this API | The key was revoked, or the plan does not include this API. Do not retry. | | 422 | Invalid parameters | The body names the offending parameter. Fix the request. Do not retry unchanged. | | 429 | Rate limit exceeded | Wait until `X-RateLimit-Reset`, then retry. Back off exponentially. | | 5xx | Server-side problem | Safe to retry with exponential backoff and jitter; all documented endpoints are idempotent. | Retry rule of thumb: retry `429` and `5xx`, never `4xx` other than `429`. ## Rate limits Every response carries the current limit state, so a client never has to guess: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests per minute allowed on this plan | | `X-RateLimit-Remaining` | Requests left in the current minute | | `X-RateLimit-Reset` | Unix timestamp when the window resets | Per-minute rates and monthly quotas vary by plan; see https://www.geoverio.com/pricing. ## API index - **IP Lookup** (`ip-lookup`) — base URL `https://ip.geoverio.com` - **Sales Tax Finder** (`sales-tax`) — base URL `https://tax.geoverio.com` - **Address Autocomplete** (`address-autocomplete`) — base URL `https://autocomplete.geoverio.com` - **Payroll** (`payroll`) — base URL `https://payroll.geoverio.com` --- # IP Lookup Everything about an IP address in one call: geolocation, the network and owner (ASN, prefix, registry), what kind of connection it is (residential, mobile, business, hosting), and whether it is a VPN, proxy, Tor exit, iCloud Private Relay, datacenter, crawler or a known abuser — plus a 0-100 risk score with the exact reasons behind it. Answered from our own datasets built from public internet routing and operator data; no third-party lookup sits in the request path. - Base URL: `https://ip.geoverio.com` - Auth: `Authorization: Bearer YOUR_API_KEY` (required on every request) - Human docs: https://www.geoverio.com/docs/ip-lookup ## Use it for - Fraud and abuse prevention at signup, checkout or login - Blocking or challenging VPN / proxy / Tor traffic - Geolocating visitors for content, compliance and analytics - Separating real users from bots and datacenter traffic ## Endpoints ### GET /v1/ip/{ip} **Look up a single IP** Returns the full intelligence report for one IPv4 or IPv6 address. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `ip` | path | string | yes | The IPv4 or IPv6 address to look up. | | `fields` | query | string | no | Comma-separated sections to include, so you only pay for the parsing you need. Valid sections: location, network, flags, risk, type, usage_type, address_type, anonymized, sources, reasons. ip, version and the bogon fields are always returned. Omit for the full report. | ```bash curl "https://ip.geoverio.com/v1/ip/8.8.8.8" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Example response: ```json { "ip": "8.8.8.8", "version": 4, "bogon": false, "bogon_reason": null, "bogon_name": null, "location": { "country": "US", "region": null, "district": null, "city": null, "postal": null, "latitude": 38, "longitude": -97, "timezone": "America/Chicago", "calling_code": "1", "idd_code": "1", "area_code": "316", "coordinate_source": "country_centroid", "source": "rir-allocation", "confidence": 0.6 }, "network": { "asn": 15169, "as_name": "GOOGLE - Google LLC", "as_country": "US", "as_registry": "arin", "org": "Google LLC", "isp": "Google LLC", "as_domain": "about.google", "prefix": "8.8.8.0/24", "netname": "GOGL", "assignment_cidr": "8.8.8.0/24", "routed": true, "registry": "arin", "allocated": "2023-12-28", "allocation_status": "allocated", "net_speed": "T1", "rdns": "dns.google" }, "continent": { "code": "NA", "name": "North America", "hemisphere": [ "north", "west" ] }, "country_info": { "name": "United States", "official_name": "United States of America", "alpha2_code": "US", "alpha3_code": "USA", "numeric_code": 840, "demonym": "American", "flag_emoji": "🇺🇸", "capital": "Washington D.C.", "total_area": 9372610, "population": 326687501, "currency": { "code": "USD", "name": "United States dollar", "symbol": "$" }, "language": { "code": "eng", "name": "English" }, "tld": ".us" }, "time_zone_info": { "olson": "America/Chicago", "current_time": "2026-07-30T00:46:18-05:00", "gmt_offset": -18000, "utc_offset": "-05:00", "is_dst": true, "abbreviation": "CDT", "dst_start_date": "2026-03-08", "dst_end_date": "2026-11-01", "sunrise": "06:28", "sunset": "20:40" }, "elevation": null, "weather_station": null, "mobile": null, "ads_category": { "code": "IAB19-11", "name": "Data Centers" }, "type": "hosting", "usage_type": "Data Center / Hosting / Transit", "address_type": "anycast", "anonymized": false, "flags": { "vpn": false, "vpn_provider": null, "proxy": false, "tor": false, "icloud_relay": false, "hosting": true, "mobile": false, "crawler": false, "crawler_name": null, "abuser": false, "anycast": true, "bogon": false, "is_spammer": false, "is_scanner": false, "is_botnet": false, "is_ai_crawler": false }, "risk": { "score": 15, "level": "low", "reasons": [ "hosting: +15" ] }, "reasons": [ { "flag": "hosting", "source": "x4b_datacenter", "detail": "listed datacenter range" }, { "flag": "anycast", "source": "measurement_anycast", "detail": "latency from distant probes is physically impossible for one host" } ], "sources": { "snapshot_id": 74, "snapshot_created_at": "2026-07-30T01:33:59.279660+00:00", "engine_version": "0.9.3", "layers": [ "rir", "bgp", "classification" ], "enrichment": [] } } ``` ### GET /v1/ip/self **Look up the caller's own IP** Same report, for the public IP the request came from (X-Forwarded-For aware). Handy for client-side "where am I / am I on a VPN" checks. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `fields` | query | string | no | Comma-separated sections to include, so you only pay for the parsing you need. Valid sections: location, network, flags, risk, type, usage_type, address_type, anonymized, sources, reasons. ip, version and the bogon fields are always returned. Omit for the full report. | ```bash curl "https://ip.geoverio.com/v1/ip/self" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### POST /v1/ip/batch **Look up many IPs at once** Send up to 100 IPs in one call. Body: {"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}. Each result is returned in order; an invalid IP comes back with an "error" object instead of failing the batch. ```bash curl -X POST "https://ip.geoverio.com/v1/ip/batch" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` ## Response fields - `ip / version` — The address you asked about, echoed back, and 4 or 6 for the IP version. Useful when you are looping over a batch and need to match results to inputs. - `bogon / bogon_reason / bogon_name` — true when the address can never appear on the public internet — private ranges (10.x, 192.168.x), loopback, link-local, documentation ranges and unallocated space. bogon_reason and bogon_name tell you which category it is. A bogon in your traffic almost always means you are reading the wrong header and seeing your own load balancer instead of the visitor. - `location` — country, region, district, city, postal, latitude, longitude and timezone, plus calling_code / idd_code / area_code for the phone numbering plan. source names the layer that produced it (geofeed, measurement, cloud, whois, rdns or rir-allocation, best first) and confidence is 0-1. coordinate_source separately tells you whether the coordinates are a real city location or a country centroid — treat country_centroid as "we know the country, not the city". - `network` — The owning network: asn, as_name, org and isp (human-readable owner), as_domain, as_country, announced prefix, netname, assignment_cidr, routed, registry / as_registry (the RIR), allocated date, allocation_status, rdns (reverse-DNS hostname) and net_speed (a coarse T1 / DSL / Mobile bucket inferred from the connection type). - `continent` — code (NA, EU, AS…), name, and hemisphere as a pair like ["north","west"]. - `country_info` — Everything about the country in one object: name, official_name, alpha2_code, alpha3_code, numeric_code, demonym, flag_emoji, capital, population, total_area, tld, plus currency {code, name, symbol} and language {code, name}. Enough to localise a checkout without a second lookup. - `time_zone_info` — olson name, current_time at that location, gmt_offset in seconds, utc_offset as ±HH:MM, is_dst, abbreviation (CDT, PST…), dst_start_date / dst_end_date, and sunrise / sunset. Handy for "do not email this user at 3am" logic. - `elevation / weather_station / mobile` — Derived extras, each null when we cannot determine it precisely enough: elevation in metres, the nearest NOAA weather_station, and mobile carrier details (name, MCC, MNC) for cellular addresses. - `ads_category` — IAB content category for the network, as {code, name} — for example {"code":"IAB19-11","name":"Data Centers"}. null when no category applies. - `type` — Connection type as a machine-readable slug: residential, mobile, business, hosting, education, government, or unknown. This is the field to branch on in code. - `usage_type` — The same thing as a display label ("Data Center / Hosting / Transit", "Residential / Fixed Line ISP", "Mobile / Cellular"). Show this one to humans; branch on type. - `address_type` — unicast, anycast, multicast or reserved. anycast means the address is announced from many physical locations at once, so a single city-level answer would be meaningless for it. - `anonymized` — true when the address belongs to any anonymising service — VPN, proxy, Tor or iCloud Private Relay. One boolean if you do not want to read four flags. - `flags` — The booleans you act on: vpn (+ vpn_provider), proxy, tor, icloud_relay, hosting, mobile, crawler (+ crawler_name), abuser, anycast, bogon, and the abuse breakdown is_spammer, is_scanner, is_botnet, is_ai_crawler. - `risk` — score 0-100, level (minimal under 15, low 15-39, medium 40-74, high 75+), and reasons listing each signal with the points it contributed, e.g. "vpn (NordVPN): +25". Weights are tuned for fraud prevention: confirmed abuse dominates, anonymisers are high, hosting is a mild signal, clean residential is ~0. - `reasons` — The full evidence trail: every flag that was set, with the source or heuristic that set it and a human-readable detail. Nothing in the report is a black box — if you ever need to justify a block to a customer, this is the field to quote. - `sources` — Which data layers contributed (rir, bgp, classification, geofeed, cloud, whois, rdns, measurement), the snapshot_id and snapshot_created_at of the dataset that served the answer, the engine_version, and any external enrichment used. Two lookups sharing a snapshot_id were answered from identical data. ## Speed, caching and freshness ### Typical response time A warm lookup answers in roughly 15 ms end to end. The core report — routing, registry, classification, risk — is served from an in-memory dataset with no database and no third-party API in the request path, so that part is sub-millisecond; the rest is network time to you. ### The slow path is bounded City-level detail sometimes needs a live registry (RDAP) or reverse-DNS call. Those are capped by a single wall-clock budget of 500 ms for the whole refinement stage, so no lookup waits on a slow registry beyond that. If the budget runs out, you still get a complete report — just with a coarser location and a lower confidence — and the outstanding fetch is allowed to finish in the background so the next caller gets the better answer. 500 ms is a ceiling, not a typical figure. ### Caching works per network, not per address Registry and geofeed answers describe a whole assignment, so they are cached against the prefix rather than the address. Looking up one address in a /22 warms all 1024 of them: the first lookup into an unfamiliar network may hit the slow path, and its neighbours come back fast. Reverse DNS is the exception — a PTR record belongs to a single address and is cached per address for 24 hours. ### You do not need to cache on your side There is no benefit to holding responses for speed, and doing so costs you accuracy: classification and routing data are recompiled continuously, so a cached report goes stale in ways a live one does not. If you do cache — for rate-limit reasons, say — key it on the address and expire within a day, and read sources.snapshot_id to tell whether two reports came from the same dataset. ### Batch is one round trip, not one hundred POST /v1/ip/batch resolves up to 100 addresses concurrently inside a single request, which is substantially faster than the same 100 lookups issued serially and counts the same against your quota. Use it whenever you have more than a handful of addresses to check at once. --- # Sales Tax Finder One GET request in, the exact US sales tax rate out. Give it any address or ZIP code and it returns the combined state + county + city + district rate, with a breakdown of every component and the data source it came from. - Base URL: `https://tax.geoverio.com` - Auth: `Authorization: Bearer YOUR_API_KEY` (required on every request) - Human docs: https://www.geoverio.com/docs/sales-tax ## Use it for - Checkout pages that need the right tax at the right address - Invoicing and accounting tools - ERP and marketplace integrations - Anything that sells across state lines ## Endpoints ### GET /v1/tax **Look up a sales tax rate** Returns the combined sales tax rate for a US address or 5-digit ZIP code. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `address` | query | string | yes | A free-form US address ("233 S Wacker Dr, Chicago IL") or just a 5-digit ZIP code ("60606"). Full addresses give rooftop-level precision; ZIPs give ZIP-level rates. | | `refresh` | query | boolean | no | Set to true to skip the cache and recalculate from source data. You rarely need this — rates are refreshed automatically. | ```bash curl "https://tax.geoverio.com/v1/tax?address=233+S+Wacker+Dr%2C+Chicago%2C+IL+60606" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Example response: ```json { "query": "233 S Wacker Dr, Chicago, IL 60606", "cached": true, "geo": { "city": "Chicago", "state": "IL", "county": "Cook", "latitude": 41.878916229496, "zip_code": "60606", "longitude": -87.636602795305, "county_fips": "17031", "input_query": "233 S Wacker Dr, Chicago, IL 60606", "matched_address": "233 S WACKER DR, CHICAGO, IL, 60606" }, "rate": { "notes": "", "state": "IL", "source": "il_official_address_file", "status": "resolved", "city_name": "Chicago", "city_rate": null, "districts": [], "confidence": "zip-level", "local_rate": 0.0425, "state_name": "Illinois", "state_rate": 0.0625, "total_rate": 0.105, "county_name": "Cook", "county_rate": null, "federal_rate": 0, "jurisdiction": "60606", "effective_date": "", "official_source_url": "https://tax.illinois.gov/research/taxrates/machine-readable-file-address-specific.html", "special_district_rate": null, "breakdown": [ { "label": "Illinois State", "rate": 0.0625, "percent": "6.25%" }, { "label": "Local", "rate": 0.0425, "percent": "4.25%" } ], "summary": "Total Sales Tax = 10.5%\n Illinois State: 6.25%\n Local: 4.25%\nJurisdiction: 60606 (confidence: zip-level)", "total_percent": "10.5%" }, "fetched_at": "2026-07-15T18:05:55.424828+00:00", "extra": { "cache_key": "IL|60606|233swackerdrchicagoil60606", "next_refresh_due": "2026-10-01T00:00:00+00:00", "coverage": { "mechanism": "state_file", "needs_key": false, "status": "live" } } } ``` ## Response fields - `rate.total_rate` — The number you want: the combined rate as a decimal. 0.105 means 10.5% — multiply it by the sale amount. This already includes state, county, city and any special districts. - `rate.total_percent` — The same figure pre-formatted for display ("10.5%"), so you never have to decide how many decimal places to round to. - `rate.breakdown` — A display-ready list of the components that add up to the total, each with label, rate and percent — for example [{"label":"Illinois State","rate":0.0625,"percent":"6.25%"},{"label":"Local","rate":0.0425,"percent":"4.25%"}]. This is what to render on an invoice line. - `rate.state_rate / local_rate` — The state component and everything local (county + city + districts) rolled together, as decimals. - `rate.city_rate / county_rate / special_district_rate / districts` — The local component split out, when the state publishes it that way. These are null where the source only gives a combined local figure — that is a property of the state, not a gap in the data, and local_rate is still correct. - `rate.federal_rate` — Always 0. The US has no federal sales tax; the field exists so the same parsing code works if you ever point it at a country that does. - `rate.confidence` — How precisely we pinned the location down: "rooftop" (exact address match), "zip-level" (ZIP average) or "state-base" (fallback to the state rate). Pass a full street address rather than a bare ZIP to get rooftop. - `rate.status` — "resolved" means a real calculated rate you can charge. "manual_lookup" means the jurisdiction cannot be automated and we hand you official_source_url instead of guessing a number. - `rate.jurisdiction / state / state_name / city_name / county_name` — Which taxing jurisdiction the rate belongs to, in both code and human-readable form. - `rate.source / official_source_url / effective_date / notes` — The audit trail: which government dataset the rate came from, the public URL of that source, the date the rate took effect, and any caveat the source attaches. Keep official_source_url with your records — it is what you show an auditor. - `rate.summary` — The whole answer as a ready-to-print multi-line string, including the components and the confidence. Useful for logs, receipts and support tickets. - `geo` — How we understood the address: matched_address (what we actually matched), input_query (what you sent), city, county, county_fips, state, zip_code, latitude and longitude. Compare matched_address with your input to catch typos before you charge someone. - `query` — The address string exactly as you sent it, echoed back so a queued or batched job can match responses to requests. - `cached` — true when the answer came from the lookup cache (sub-millisecond). Caching does not make a rate less current — see the notes below. - `fetched_at` — When the underlying rate was last pulled from the government source, as an ISO 8601 timestamp. This is the age of the data, not the age of your request. - `extra` — Operational detail: cache_key, next_refresh_due (when we will next re-read the source), and coverage {status, mechanism, needs_key} describing how this state is sourced. Safe to ignore in normal use. ## Getting the rate right ### Send the full address, not just the ZIP A ZIP code is a mail-delivery route, not a tax boundary, and plenty of ZIPs straddle two or three jurisdictions. Sending "60606" returns the ZIP average and confidence "zip-level"; sending "233 S Wacker Dr, Chicago, IL 60606" returns the exact rate for that building and confidence "rooftop". If you are charging real money, send the whole address — you already have it at checkout. ### Always check rate.status before you charge A handful of jurisdictions publish rates in a form nobody can automate reliably. Rather than invent a plausible number, those come back with status "manual_lookup" and an official_source_url. Treat anything other than status "resolved" as "do not auto-charge" — branch on it once and you will never be surprised by a wrong rate in production. ### Cached does not mean stale cached: true only means we did not recompute the answer for this request. Rates are re-read from the government sources on a schedule (extra.next_refresh_due tells you when this one is next due) and the cache is dropped the moment a source changes, so a cached answer is the same answer a fresh one would give. The ?refresh=true parameter exists for debugging; you do not need it in normal operation. ### Every rate traces back to a government source The API never estimates or interpolates a rate. Each answer carries the source dataset and its public URL, so a number you charged can always be traced back to the state or county publication it came from. Store official_source_url and fetched_at with your transaction records and your sales-tax audit trail is complete. ### Pair it with Address Autocomplete Every Address Autocomplete suggestion carries a tax_query field: a normalised address string built specifically to be passed straight into this API. Let the customer pick their address from the typeahead, then send suggestion.tax_query here — you get a rooftop-confidence rate and the customer never typed an address you had to guess at. --- # Address Autocomplete Type-ahead address suggestions from 200+ million US addresses, in a few milliseconds. Wire it to your address field and users pick their verified address in 3-4 keystrokes — with city, state, ZIP and coordinates filled in automatically. - Base URL: `https://autocomplete.geoverio.com` - Auth: `Authorization: Bearer YOUR_API_KEY` (required on every request) - Human docs: https://www.geoverio.com/docs/address-autocomplete ## Use it for - Checkout and signup forms - Delivery address entry - CRM data entry that stays clean - Anything that hates typos in addresses ## Endpoints ### GET /v1/autocomplete **Get address suggestions** Returns ranked address suggestions for a partial query. Call it on every keystroke after the 2nd character. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `q` | query | string | yes | What the user has typed so far — part of a street address, a city name, or a ZIP prefix. Minimum 2 characters. | | `limit` | query | integer | no | Maximum suggestions to return (1-20). Default is 8 — a good dropdown size. | | `state` | query | string | no | Two-letter state code (e.g. CA) to bias results toward that state. If omitted, we auto-detect the caller's state from their IP so nearby addresses rank first. | ```bash curl "https://autocomplete.geoverio.com/v1/autocomplete?q=1600+amph" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Example response: ```json { "query": "1600 AMPH", "count": 3, "suggestions": [ { "label": "1600 Amphitheatre Py, Mountain View, CA 94043", "house": "1600", "street": "Amphitheatre Py", "city": "Mountain View", "state": "CA", "zip": "94043", "lat": 37.4220221, "lon": -122.0842902, "tax_query": "1600 AMPHITHEATRE PY MOUNTAIN VIEW CA 94043" }, { "label": "1600 Amphitheatre Pkwy, Mountain View, CA 94043", "house": "1600", "street": "Amphitheatre Pkwy", "city": "Mountain View", "state": "CA", "zip": "94043", "lat": 37.4220018, "lon": -122.0849364, "tax_query": "1600 AMPHITHEATRE PKWY MOUNTAIN VIEW CA 94043" }, { "label": "1600 Amphitheatre Parkway, Mountain View, CA 94043", "house": "1600", "street": "Amphitheatre Parkway", "city": "Mountain View", "state": "CA", "zip": "94043", "lat": 37.4230075, "lon": -122.0830662, "tax_query": "1600 AMPHITHEATRE PARKWAY MOUNTAIN VIEW CA 94043" } ] } ``` ## Response fields - `suggestions[].label` — The full address formatted for display, ready to drop into a dropdown row: "1600 Amphitheatre Pkwy, Mountain View, CA 94043". - `suggestions[].house / street / city / state / zip` — The same address already split into components, so you can fill five form fields from one pick instead of parsing the label yourself. - `suggestions[].lat / lon` — Coordinates for the address, for map pins, distance checks and delivery-zone logic. - `suggestions[].tax_query` — A normalised, upper-cased address string built to be passed straight into the Sales Tax Finder API. Pick an address here, send this string there, and you get a rooftop-confidence tax rate with no address parsing in between. - `count` — How many suggestions came back. 0 means no match — show your "enter it manually" fallback rather than an empty dropdown. - `query` — The normalised form of what you sent. Because typeahead responses can arrive out of order, compare this against the current input before rendering — if it no longer matches what the user has typed, drop the response. ## Wiring it into a real form ### Debounce, and drop out-of-order responses Calling on every keystroke is what this API is built for, but the network is not ordered: a request for "160" can easily land after the one for "1600 amph". Debounce roughly 120-150 ms, and before rendering compare the response's query field against the current input — if they differ, throw the response away. Those two rules are the difference between a typeahead that feels instant and one that flickers. ### Results are biased to the caller's state automatically With no ?state= parameter we detect the caller's US state from their IP and rank nearby addresses first, so the right suggestion usually appears within three or four keystrokes. If you proxy these calls through your own backend, the API sees your server's IP rather than the user's — either forward the real address in X-Forwarded-For, or pass ?state= explicitly. ### Keep your API key on the server A typeahead runs in the browser, which makes it the easiest place to leak a key. Call this API from a thin endpoint on your own backend that adds the Authorization header, and let the browser talk only to your endpoint. Never ship gv_live_ keys to the client. ### Sensible limits q must be at least 2 characters; limit accepts 1-20 and defaults to 8, which is about as many rows as a dropdown can show before it stops being scannable. Asking for 20 costs no more than asking for 8, but it does give the user more to read. --- # Payroll US payroll, gross to net, in one call: federal income tax on the IRS percentage method, FICA with the wage-base and Additional Medicare thresholds handled across the year, state and local withholding, and the employee-paid disability and paid-leave programs. You get back every tax and deduction, net pay, updated year-to-date figures, a render-ready pay statement your own template can print by iterating, and the items the employee's state legally requires on a wage statement. Nothing is stored: year-to-date figures travel with the request, request bodies are never logged, and no employer or employee record is kept. - Base URL: `https://payroll.geoverio.com` - Auth: `Authorization: Bearer YOUR_API_KEY` (required on every request) - Human docs: https://www.geoverio.com/docs/payroll ## Use it for - Paystub and payroll products that need the numbers but keep their own design - Running a monthly pay run for a company's own employees - Bookkeeping and accounting tools that reconcile take-home pay - Answering "what would I actually take home" for a salary or offer ## Endpoints ### GET /v1/paycheck/quick **Quick take-home estimate** The one-line version: what actually lands in the employee's account. Everything not supplied is defaulted, so this is an estimate - use POST /v1/paycheck for a real pay run. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `gross` | query | number | yes | Gross pay for one pay period. | | `frequency` | query | string | no | weekly, biweekly, semimonthly or monthly. Defaults to biweekly. | | `state` | query | string | no | Two-letter work state. Defaults to TX. | | `filing_status` | query | string | no | single_or_mfs, married_jointly or head_of_household. | | `state_allowances` | query | integer | no | Allowances or exemptions claimed on the state withholding certificate. | ```bash curl "https://payroll.geoverio.com/v1/paycheck/quick?gross=5000" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Example response: ```json { "meta": { "tax_year": 2026, "data_version": "2026.1", "tables_effective_on": "2026-01-01", "module_version": "0.2.0", "disclaimer": "Calculated estimates for record-keeping. Not tax advice. Geoverio does not file or remit taxes and does not verify any figure supplied by the caller." }, "gross": 5000, "net_pay": 3503.56, "taxes": [ { "code": "federal_income_tax", "label": "Federal Income Tax", "amount": 732 }, { "code": "social_security", "label": "Social Security", "amount": 310 }, { "code": "medicare", "label": "Medicare", "amount": 72.5 }, { "code": "ca_income_tax", "label": "California Income Tax", "amount": 316.94 }, { "code": "ca_sdi", "label": "CA SDI", "amount": 65 } ], "effective_tax_rate": 29.93, "jurisdiction": { "code": "CA", "name": "California", "status": "supported", "method": "ca_method_b", "effective_from": "2026-01-01", "effective_to": null, "source": { "name": "California Withholding Schedules for 2026 - Method B, Exact Calculation (EDD)", "url": "https://edd.ca.gov/siteassets/files/pdf_pub_ctr/26methb.pdf", "retrieved": "2026-08-31", "verified_by": "Golden tests reproduce the publication's annualized worked examples E and F." } }, "warnings": [ { "code": "jurisdiction_note", "severity": "info", "message": "California taxes HSA contributions - they reduce federal wages but not California wages." } ], "note": "An estimate with default assumptions. POST /v1/paycheck for a real pay run." } ``` ### POST /v1/paycheck **Calculate one paycheck** The real thing. Send earnings, W-4 data, jurisdictions, pre-tax deductions and year-to-date figures; get every tax, net pay, updated YTD, the render-ready pay statement and the jurisdiction's statement requirements. ```bash curl -X POST "https://payroll.geoverio.com/v1/paycheck" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` Example response: ```json { "meta": { "tax_year": 2026, "data_version": "2026.1", "tables_effective_on": "2026-01-01", "module_version": "0.1.0", "disclaimer": "Calculated estimates for record-keeping. Not tax advice. Geoverio does not file or remit taxes and does not verify any figure supplied by the caller." }, "pay_statement": { "period": { "start": "2026-09-01", "end": "2026-09-15", "pay_date": "2026-09-15", "frequency": "semimonthly" }, "sections": [ { "key": "earnings", "label": "Earnings", "columns": [ "rate", "hours", "current", "ytd" ], "lines": [ { "key": "regular", "label": "Regular", "current": 2800, "ytd": 2800, "formatted": { "current": "$2,800.00", "ytd": "$2,800.00" }, "rate": 35, "hours": 80 }, { "key": "overtime", "label": "Overtime", "current": 210, "ytd": 210, "formatted": { "current": "$210.00", "ytd": "$210.00" }, "rate": 52.5, "hours": 4 } ], "total": { "key": "gross_pay", "label": "Gross Pay", "current": 3010, "ytd": 53010, "formatted": { "current": "$3,010.00", "ytd": "$53,010.00" } } }, { "key": "pre_tax_deductions", "label": "Pre-Tax Deductions", "columns": [ "current", "ytd" ], "lines": [ { "key": "retirement_401k", "label": "401(k)", "current": 150, "ytd": 150, "formatted": { "current": "$150.00", "ytd": "$150.00" } }, { "key": "section125", "label": "Section 125 (pre-tax benefits)", "current": 100, "ytd": 100, "formatted": { "current": "$100.00", "ytd": "$100.00" } } ], "total": { "key": "pre_tax_total", "label": "Total Pre-Tax", "current": 250, "ytd": null, "formatted": { "current": "$250.00", "ytd": null } } }, { "key": "taxes", "label": "Taxes Withheld", "columns": [ "current", "ytd" ], "lines": [ { "key": "federal_income_tax", "label": "Federal Income Tax", "current": 240, "ytd": 240, "formatted": { "current": "$240.00", "ytd": "$240.00" } }, { "key": "social_security", "label": "Social Security", "current": 180.42, "ytd": 180.42, "formatted": { "current": "$180.42", "ytd": "$180.42" } }, { "key": "medicare", "label": "Medicare", "current": 42.2, "ytd": 42.2, "formatted": { "current": "$42.20", "ytd": "$42.20" } }, { "key": "ca_income_tax", "label": "California Income Tax", "current": 95.05, "ytd": 95.05, "formatted": { "current": "$95.05", "ytd": "$95.05" } }, { "key": "ca_sdi", "label": "CA SDI", "current": 37.83, "ytd": 37.83, "formatted": { "current": "$37.83", "ytd": "$37.83" } } ], "total": { "key": "taxes_total", "label": "Total Taxes", "current": 595.5, "ytd": null, "formatted": { "current": "$595.50", "ytd": null } } } ], "net_pay": { "key": "net_pay", "label": "Net Pay", "current": 2164.5, "ytd": 2164.5, "formatted": { "current": "$2,164.50", "ytd": "$2,164.50" } } }, "gross": 3010, "net_pay": 2164.5, "totals": { "gross": 3010, "pre_tax_deductions": 250, "taxes": 595.5, "post_tax_deductions": 0, "net_pay": 2164.5 }, "wage_bases": { "federal_income_tax": 2760, "fica": 2910, "state_income_tax": 2760 }, "taxes": [ { "code": "federal_income_tax", "label": "Federal Income Tax", "amount": 240 }, { "code": "social_security", "label": "Social Security", "amount": 180.42 }, { "code": "medicare", "label": "Medicare", "amount": 42.2 }, { "code": "ca_income_tax", "label": "California Income Tax", "amount": 95.05 }, { "code": "ca_sdi", "label": "CA SDI", "amount": 37.83 } ], "programs": [ { "code": "ca_sdi", "label": "CA SDI", "amount": 37.83, "rate": 0.013, "taxable_wages": 2910, "wage_cap": null, "skipped": false, "source_url": "https://edd.ca.gov/en/payroll_taxes/rates_and_withholding/" } ], "locals": [], "ytd_out": { "gross": 53010, "federal_income_tax": 240, "ss_wages": 52910, "ss_tax": 180.42, "medicare_wages": 52910, "medicare_tax": 42.2, "additional_medicare": 0, "state_income_tax": 95.05, "net_pay": 2164.5, "supplemental_wages": 0, "ca_sdi": 37.83, "ca_sdi_wages": 2910 }, "employer": { "social_security": 180.42, "medicare": 42.2, "futa": 52.38, "futa_rate": 0.018, "futa_taxable": 2910 }, "reporting": { "qualified_tips": 0, "qualified_overtime": 0, "ttoc": null, "note": "Qualified tips and qualified overtime are reported (Form W-2 box 12 codes TP and TT, box 14b) but remain fully subject to income tax withholding, Social Security, Medicare and FUTA. Qualified overtime is the FLSA half-time premium only, not total overtime pay." }, "jurisdiction": { "code": "CA", "name": "California", "status": "supported", "method": "ca_method_b", "effective_from": "2026-01-01", "effective_to": null, "source": { "name": "California Withholding Schedules for 2026 - Method B, Exact Calculation (EDD)", "url": "https://edd.ca.gov/siteassets/files/pdf_pub_ctr/26methb.pdf", "retrieved": "2026-08-31", "verified_by": "Golden tests reproduce the publication's annualized worked examples E and F." } }, "warnings": [ { "code": "jurisdiction_note", "severity": "info", "message": "California taxes HSA contributions - they reduce federal wages but not California wages." } ], "compliance": { "statement_requirements": { "jurisdiction": "CA", "authority": "California Labor Code section 226(a)", "source_url": "https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=LAB§ionNum=226", "required_items": [ { "key": "gross_wages", "description": "Gross wages earned.", "satisfied": true, "conditional": false, "note": null }, { "key": "total_hours_worked", "description": "Total hours worked by the employee (non-exempt employees).", "satisfied": true, "conditional": false, "note": null }, { "key": "piece_rate_units", "description": "The number of piece-rate units earned and the applicable piece rate, if the employee is paid on a piece-rate basis.", "satisfied": false, "conditional": true, "note": "Only the caller holds this." }, { "key": "all_deductions", "description": "All deductions, itemized.", "satisfied": true, "conditional": false, "note": null }, { "key": "net_wages", "description": "Net wages earned.", "satisfied": true, "conditional": false, "note": null }, { "key": "pay_period_dates", "description": "The inclusive dates of the period for which the employee is paid.", "satisfied": true, "conditional": false, "note": null }, { "key": "employee_identification", "description": "The name of the employee and the last four digits of their social security number or an employee identification number.", "satisfied": false, "conditional": false, "note": "Only the caller holds this; never send a full SSN." }, { "key": "employer_identification", "description": "The name and address of the legal entity that is the employer.", "satisfied": false, "conditional": false, "note": "Only the caller holds this." }, { "key": "hourly_rates_and_hours", "description": "All applicable hourly rates in effect during the pay period and the corresponding number of hours worked at each hourly rate.", "satisfied": true, "conditional": false, "note": null } ], "all_satisfied": false, "unsatisfied": [ "employee_identification", "employer_identification" ], "note": "California also requires available paid sick leave to be shown on the statement or a separate writing (Labor Code section 246(i)); that balance is not payroll-tax data and must come from the caller." } } } ``` ### POST /v1/paycheck/batch **Calculate a whole pay run** Up to 200 employees in one call. Send shared fields once in "default" and only what differs per employee. One employee failing (an unsupported jurisdiction, say) never fails the run - it comes back in "errors" with the reason. Each employee counts as one calculation. ```bash curl -X POST "https://payroll.geoverio.com/v1/paycheck/batch" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` ### POST /v1/paycheck/series **Back-fill a run of pay periods** One employee, many periods, one call. Give a date range, a frequency and the earnings that repeat; get every pay period back with year-to-date chained across them. Periods are generated the way the calendar falls (semimonthly is the 1st-15th and 16th-to-month-end, quarters start in January), and a period that has not finished yet is never generated. Use overrides for the weeks that differ - a bonus, extra hours. ```bash curl -X POST "https://payroll.geoverio.com/v1/paycheck/series" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` ### POST /v1/gross-up **Gross-up: the gross that produces a target net** "Give them $1,000 in hand - what does that cost in gross?" Solved by running the full engine, so wage-base caps and bracket edges are respected rather than approximated. ```bash curl -X POST "https://payroll.geoverio.com/v1/gross-up" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` ### POST /v1/employer-cost **What the employee costs the employer** The employer side: Social Security and Medicare matches, FUTA including any state credit reduction, and SUTA from a rate you supply (state unemployment rates are per-employer and are not public data). ```bash curl -X POST "https://payroll.geoverio.com/v1/employer-cost" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` ### POST /v1/statement/validate **Check a pay statement against its jurisdiction** Does this stub satisfy the wage-statement law where the employee works? Returns the required items, which you already supply, and which are still missing. ```bash curl -X POST "https://payroll.geoverio.com/v1/statement/validate" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` ### GET /v1/jurisdictions **Coverage matrix** Every jurisdiction with its status - supported, partial or unsupported - the method used, the official source behind it, and the local jurisdictions covered. Read this before assuming a state works. ```bash curl "https://payroll.geoverio.com/v1/jurisdictions" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### GET /v1/limits/{year} **Federal rates and limits** The year's Social Security wage base, Medicare and Additional Medicare thresholds, FUTA rates and credit reductions, supplemental rates, and the 401(k), HSA, FSA and commuter limits. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `year` | path | integer | yes | Tax year. | ```bash curl "https://payroll.geoverio.com/v1/limits/2026" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Response fields - `meta` — tax_year, data_version and tables_effective_on - the exact tables that produced this answer, so any calculation can be reproduced later. Also the disclaimer you should carry on the document. - `pay_statement` — The render-ready statement: ordered sections (earnings, pre-tax deductions, taxes, post-tax deductions), each with labelled lines carrying current and YTD amounts plus pre-formatted strings. Loop over it and print - your template needs no payroll knowledge of its own. - `wage_bases` — The three taxable wage bases, which are NOT the same number: federal income tax, FICA, and state income tax. A 401(k) deferral reduces the first but not the second; Pennsylvania taxes it in the third. - `taxes` — Every withholding line with a stable code and a display label: federal income tax, Social Security, Medicare, Additional Medicare when it applies, state income tax, state disability and paid-leave programs, and local taxes. - `ytd_out` — Updated year-to-date figures. Store these on your side and send them back next period: they are what makes the Social Security cap and the Additional Medicare threshold come out right in December. - `employer` — The employer side of the same paycheck: Social Security and Medicare matches and FUTA with any state credit reduction applied. - `reporting` — qualified_tips, qualified_overtime and the Treasury Tipped Occupation Code - the new 2026 Form W-2 reporting (box 12 codes TP and TT, box 14b). Neither reduces taxable wages, and qualified overtime is the FLSA half-time premium only, not total overtime pay. - `compliance.statement_requirements` — What the employee's jurisdiction legally requires on a wage statement, which items this response already satisfies, and which only you can supply (employee name, employer address, the last four of the SSN). This is the difference between numbers and a compliant pay stub. - `warnings` — Things worth knowing that never change the math: a deduction annualizing past its IRS limit, a state quirk that applies to this employee, deductions exceeding available pay. ## How to use this API well ### Year-to-date is an input, not just an output Send the ytd block every time. Social Security stops at the annual wage base and Additional Medicare starts above a year-to-date threshold, so an engine that only sees this period is correct for eleven months and wrong in December for every high earner. Take ytd_out from each response and send it back next period. ### The three wage bases are different numbers A traditional 401(k) deferral reduces federal income tax wages but NOT Social Security and Medicare wages. Section 125 premiums reduce both. Roth reduces neither. Several states then override the federal treatment - Pennsylvania taxes 401(k) deferrals, California taxes HSA contributions, New Jersey taxes 403(b), 457 and ordinary Section 125. Send your deductions with their real codes and the engine applies the right matrix per jurisdiction. ### An unsupported jurisdiction is an error on purpose When this data version has no sourced tables for a jurisdiction you get a 422 that says exactly what is missing - never a zero. A silent zero is a wrong paycheck that nobody notices until an agency notice arrives. Call GET /v1/jurisdictions to see what is covered before you go live in a state. ### Tables are chosen by pay date, not by year Several states publish more than one table set inside one calendar year - Ohio changed on 1 August 2026, Utah on 1 June, Georgia retroactively, Arkansas in May. Send the real pay_date and the engine picks the tables that were in force; meta.tables_effective_on tells you which ones it used. ### Batch and series are not the same thing POST /v1/paycheck/batch is many employees in ONE pay period - this Friday's payroll for forty people. POST /v1/paycheck/series is ONE employee across many periods - reconstructing a year of pay stubs. They are different because periods are not independent: year-to-date has to chain from one to the next, which is why the series endpoint does that chaining for you rather than leaving it to your loop. ### We calculate; we do not file, remit, or verify There is no money movement and no filing here, and nothing you send is stored or verified. Keep the disclaimer on any document you generate, and make sure your terms prohibit using generated pay stubs as fabricated income verification - that is the one thing this category of product gets misused for. ### Never send a full SSN employee_ref is your own opaque reference and the API rejects anything that looks like a Social Security number. Pay statements should print at most the last four digits, and that belongs in your template, not in a request to us. --- ## Choosing an API | The task | Use | |---|---| | How much sales tax to charge for a US address | Sales Tax Finder, `GET /v1/tax` | | Suggest US addresses as the user types | Address Autocomplete, `GET /v1/autocomplete` | | Validate and normalise an address a user typed | Address Autocomplete, then use the top suggestion | | Where is this visitor, and is it a VPN or bot | IP Lookup, `GET /v1/ip/{ip}` | | Score a signup or checkout for fraud risk | IP Lookup, read `risk.score` and `flags` | | Classify many IPs from a log file | IP Lookup, `POST /v1/ip/batch` (100 per call) | ## Recipe: address field to tax rate The two APIs are designed to hand off to each other. Every autocomplete suggestion carries `tax_query`, a normalised address string built to be passed straight into the tax endpoint — so no address parsing happens in your code. ```javascript // Server-side. The key never reaches the browser. const KEY = process.env.GEOVERIO_API_KEY; const auth = { Authorization: `Bearer ${KEY}` }; // 1. As the user types (debounce ~150ms), fetch suggestions. async function suggest(typed) { const url = new URL('https://autocomplete.geoverio.com/v1/autocomplete'); url.searchParams.set('q', typed); url.searchParams.set('limit', '8'); const res = await fetch(url, { headers: auth }); if (!res.ok) throw new Error(`autocomplete ${res.status}`); const data = await res.json(); // Responses can arrive out of order — drop stale ones. return data.query === normalize(typed) ? data.suggestions : null; } // 2. Once the user picks one, get the tax rate for that exact address. async function taxFor(suggestion) { const url = new URL('https://tax.geoverio.com/v1/tax'); url.searchParams.set('address', suggestion.tax_query); const res = await fetch(url, { headers: auth }); if (!res.ok) throw new Error(`tax ${res.status}`); const { rate } = await res.json(); // Never charge on an unresolved rate — send it for manual review. if (rate.status !== 'resolved') { return { needsReview: true, source: rate.official_source_url }; } return { rate: rate.total_rate, // 0.105 display: rate.total_percent, // "10.5%" lines: rate.breakdown, // per-jurisdiction rows for the invoice }; } ``` Why this order matters: passing a bare ZIP to the tax endpoint returns a ZIP-average rate (`confidence: "zip-level"`), because ZIP codes are mail routes and routinely straddle tax jurisdictions. Going through autocomplete first yields a full street address and `confidence: "rooftop"` — the rate for that specific building. ## Recipe: risk-scoring a signup ```python import os, requests AUTH = {"Authorization": f"Bearer {os.environ['GEOVERIO_API_KEY']}"} def check(ip: str) -> dict: # `fields` trims the payload to what you actually branch on. r = requests.get( f"https://ip.geoverio.com/v1/ip/{ip}", params={"fields": "flags,risk,location,type"}, headers=AUTH, timeout=5, ) r.raise_for_status() d = r.json() # A bogon means you are reading the wrong header and seeing your # own load balancer, not the visitor. Fix the plumbing, don't block. if d["bogon"]: return {"action": "allow", "why": "private/internal address"} if d["flags"]["abuser"] or d["risk"]["score"] >= 75: return {"action": "block", "why": d["risk"]["reasons"]} if d["anonymized"] or d["risk"]["level"] == "medium": return {"action": "challenge", "why": d["risk"]["reasons"]} return {"action": "allow", "why": []} ``` Notes for an implementer: - Use `POST /v1/ip/batch` (up to 100 addresses) instead of a loop — it resolves them concurrently inside one request and costs the same against quota. - Do not cache responses for speed. Classification and routing data are recompiled continuously, so a cached verdict goes stale in ways a live one does not. - `flags.crawler` marks known-good bots (search engines). Blocking on risk score alone can de-index a site; check `flags.crawler` first. - Every verdict is explainable: `reasons` lists each flag with the source that set it. Log it, and you can always justify a block. ## Things that will bite an implementer - A `gv_live_` key in front-end code is public. Proxy through your own backend. - Charging on `rate.status != "resolved"` means charging a number the API explicitly declined to calculate. Branch on it. - A ZIP code is not a tax jurisdiction. Send the full street address when money depends on the answer. - Typeahead responses arrive out of order. Compare the response's `query` field against the current input before rendering. - Retrying a `401`, `403` or `422` will fail identically every time. Only `429` and `5xx` are worth a retry. - Read `X-RateLimit-Remaining` and slow down before you hit `429`, rather than after. --- ## About Geoverio Geoverio provides modular APIs for IP intelligence, US sales tax, address autocomplete, and payroll data to developers building modern products in the US market. The platform uses a single bearer token to access four distinct modules through one gateway while offering a permanent free tier and predictable per-request pricing. Official profiles: - https://www.geoverio.com ## Pages - [Home](https://www.geoverio.com/) — Geoverio provides modular APIs for IP intelligence, addresses, tax data, and US payroll. - [API catalog](https://www.geoverio.com/apis) — Browse every API on the Geoverio platform. - [Pricing](https://www.geoverio.com/pricing) — Geoverio prices its APIs by volume, not by feature: every plan reaches every API, and the tiers differ on monthly request quota and rate limit. - [Documentation home](https://www.geoverio.com/docs) — Beginner-friendly guides and a full reference for every Geoverio API: authentication, endpoints, response fields and working examples you can copy. - [Blog index](https://www.geoverio.com/blog) — Practical guides and honest deep dives from the Geoverio team, written for developers who work with IPs, addresses and the messy geography behind them. - [IP Lookup — product page](https://www.geoverio.com/apis/ip-lookup) — The Geoverio IP Lookup API returns geolocation, network owner, ASN, VPN/proxy/Tor detection and a risk score for any IPv4 or IPv6 address in one JSON response. - [Sales Tax Finder — product page](https://www.geoverio.com/apis/sales-tax) — The Geoverio Sales Tax API returns current US sales tax rates for an address or ZIP code, broken down by state, county, city and special district. - [Address Autocomplete — product page](https://www.geoverio.com/apis/address-autocomplete) — The Geoverio Address Autocomplete API suggests complete, valid US addresses as a user types, tolerating typos and partial input. - [Payroll — product page](https://www.geoverio.com/apis/payroll) — The Geoverio Payroll API turns one employee's pay inputs — earnings, W-4 data, jurisdictions, pre-tax deductions and year-to-date figures — into every federal, state and local tax… - [About Geoverio](https://www.geoverio.com/page/about) — Geoverio is a small team building one API platform for the data every product eventually needs: IP intelligence, address validation and autocomplete, US sales tax and payroll. - [Changelog](https://www.geoverio.com/page/changelog) — The Geoverio changelog lists every platform and API change, newest first. - [Data Processing Addendum](https://www.geoverio.com/page/dpa) — Geoverio's data processing addendum covers the case where your API requests contain personal data belonging to your own users — you act as the controller and Geoverio as the proce… - [Privacy Policy](https://www.geoverio.com/page/privacy) — Geoverio's privacy policy explains what the platform collects, why it is collected, how long it is kept, and which processors handle it. - [Security](https://www.geoverio.com/page/security) — This page describes how Geoverio is built and operated: encryption in transit, API key handling, access control, backups and incident response. - [Talk to sales](https://www.geoverio.com/page/contact-sales) — Geoverio's self-serve plans go up to 5,000,000 requests a month. - [Terms of Service](https://www.geoverio.com/page/terms) — Geoverio's terms of service govern the APIs, dashboard and website: what an account may do, rate limits and fair use, billing and cancellation, uptime expectations and liability. ## Articles - [Calculate US Payroll with One API Call: Geoverio Payroll Module](https://www.geoverio.com/blog/calculate-us-payroll-one-api-call) — Learn how to calculate US payroll in a single API call using Geoverio’s Payroll module. - [How to Use the Geoverio IP Risk Score to Flag High‑Risk Orders](https://www.geoverio.com/blog/ip-risk-score-flag-high-risk-orders) — Learn step‑by‑step how to apply Geoverio’s IP risk score in your checkout flow. - [Combining IP Lookup & Sales Tax Finder for Real‑Time Checkout Compliance](https://www.geoverio.com/blog/combine-ip-lookup-sales-tax-finder) — Learn how to pair Geoverio’s IP Lookup and Sales Tax Finder APIs to enforce e‑commerce compliance instantly at checkout, with one API key and a clear pricing model. - [vpn detection api: real‑time IP Lookup for fraud prevention](https://www.geoverio.com/blog/vpn-detection-api-real-time-ip-lookup) — Learn how to integrate Geoverio’s IP Lookup API to spot VPN, proxy, and Tor usage instantly. - [Validate US Addresses Before Sales Tax Requests](https://www.geoverio.com/blog/validate-us-addresses-before-sales-tax-requests) — Use our address validation API to clean inputs before sending sales tax calls. - [How multi-state payroll APIs automate tax compliance](https://www.geoverio.com/blog/how-multi-state-payroll-apis-automate-tax-compliance) — Stop updating spreadsheets. - [Five places payroll math goes quietly wrong](https://www.geoverio.com/blog/five-places-payroll-math-goes-quietly-wrong) — A wrong paycheck looks exactly like a right one until somebody's W-2 is wrong in February. - [What an IP geolocation API can and can't tell you](https://www.geoverio.com/blog/what-an-ip-geolocation-api-can-and-cant-tell-you) — IP location data comes from routing records, not GPS. ## Glossary - **address normalization** — A process that standardizes inconsistent street inputs into a single, machine-readable format used by postal services for delivery. - **ASN** — An Autonomous System Number uniquely identifies a network block on the internet and reveals its owner or provider. - **effective tax rate** — The actual percentage of taxable income paid after accounting for all credits and deductions specific to a business location. - **federal withholding amount** — The specific tax dollars deducted from an employee's paycheck to cover their estimated annual federal income tax liability. - **ISP** — The Internet Service Provider associated with an IP address shows which company owns the network segment you are connecting to. - **locality rules** — The varying tax thresholds or physical footprints required in specific states before a business must register for local tax collection. - **net pay** — The final amount deposited into an employee's bank account after all required deductions like taxes and insurance are removed. - **network owner lookup** — The process of identifying the organization or ISP associated with an IP address and displaying their contact details for abuse reporting. - **postal carrier delivery point** — The final physical location where a mail package stops on its route, often derived from zip plus four codes but confirmed by carrier routes. - **proxy IP** — An address that forwards requests from a user without revealing their true origin, commonly used for anonymity but detectable via header analysis. - **risk score** — A numeric value that rates how likely an IP is used for fraud based on historical abuse data, blacklists, and geographic anomalies. - **sales nexus** — The specific physical or digital connection between a company and a state that legally requires you to collect and remit sales tax there. - **street center point** — A precise latitude and longitude coordinate calculated from the midpoint of a physical street address block for accurate mapping. - **Tor exit node** — A server on the Tor network where encrypted traffic emerges from the dark web before heading to its final destination, often flagged by our risk models. - **validation confidence score** — A metric between zero and one that indicates how certain our system is about the accuracy of an entered street or ZIP code. - **VPN detection** — This check identifies if an IP address hides behind a Virtual Private Network by analyzing header patterns and network behavior. - **Gross-up** — Working backwards from the amount an employee should receive to the gross that produces it after tax. Because withholding is not a flat percentage, an accurate gross-up has to run the full calculation rather than divide by an assumed rate — otherwise it drifts whenever the employee is near a wage-base cap or a bracket edge. - **Wage base** — The annual earnings ceiling above which a given payroll tax stops applying. Social Security has one; Medicare does not. Because the ceiling is annual, whether a particular paycheck crosses it depends on every paycheck before it in the year, which is why year-to-date figures have to be carried from one pay period into the next. - **Additional Medicare Tax** — An extra 0.9% withheld from an employee's wages once their year-to-date earnings pass a statutory threshold. The employer does not match it. Like the Social Security wage base it is an annual boundary, so the paycheck it starts on depends on the pay history before it. - **Pay statement** — The itemised record of a single paycheck given to the employee. No federal statute prescribes its contents, but most states do, and the required items differ by state — which means a stub can be arithmetically correct and still not lawful where the employee works. ## Reading pages as text Append `?format=md` to any page URL on this site to get the same content as Markdown, without navigation or scripts. Example: https://www.geoverio.com/pricing?format=md ## Questions and answers ### Home **Is there a free tier for Geoverio APIs?** Yes, the service offers a permanent free tier requiring no credit card. One account gets 1,000 monthly requests with community support. **How do I authenticate to the API?** You use a single bearer token that works for all four published API modules. The same key covers every endpoint without needing separate credentials. **What happens when my free quota is exceeded?** If you stop at the zero balance on the pay-as-you-go plan, no surprise bills occur. Other plans allow higher limits but charge a monthly fee. ### API catalog **What is Geoverio?** Geoverio provides modular APIs that give access to IP Lookup, Sales Tax Finder, Address Autocomplete, and Payroll. Developers use one bearer token to authenticate with all modules. **Is there a free option?** The platform includes a free tier for new users looking to test the services without immediate costs. **Do all Geoverio APIs use the same API key?** Yes. One bearer token works across every module — IP Lookup, Sales Tax, Address Autocomplete and Payroll — and every plan reaches every API. There is nothing separate to enable per module. ### Address Autocomplete — product page **What does the Address Autocomplete API do?** Fast, typo-tolerant US address autocomplete. **How do I authenticate with the Address Autocomplete API?** Send your key as a bearer token: `Authorization: Bearer `. One key works across every Geoverio API your plan includes. **Is there a free tier?** Yes. Every plan reaches every API; the free tier differs on monthly volume and rate limit, not on which endpoints you may call. ### IP Lookup — product page **What does the IP Lookup API do?** Geolocation, network owner, VPN/proxy/Tor detection and a risk score for any IP. **How do I authenticate with the IP Lookup API?** Send your key as a bearer token: `Authorization: Bearer `. One key works across every Geoverio API your plan includes. **Is there a free tier?** Yes. Every plan reaches every API; the free tier differs on monthly volume and rate limit, not on which endpoints you may call. ### Payroll API — product page **What does the Geoverio Payroll API do?** It calculates US payroll from gross to net. You send one employee's earnings, W-4 data, work jurisdictions, pre-tax deductions and year-to-date figures; it returns every federal, state and local tax, every deduction, net pay, updated year-to-date, a render-ready pay statement, and the items that employee's jurisdiction requires on a wage statement. **What is the difference between the batch and series endpoints?** Batch is many employees in one pay period, and the employees are independent, so they run side by side — up to 200 in a call. Series is one employee across many pay periods, and the periods are not independent: Social Security stops at the annual wage base and Additional Medicare starts above a threshold, so each period has to feed year-to-date into the next. Running this Friday's payroll for forty people is a batch call. Reconstructing a year of pay stubs for one person is a series call. **Does the Geoverio Payroll API store employee data?** No. Year-to-date is an input and an output, request bodies are never logged, and no employer, employee or paycheck record is kept. The API can sit inside your product without becoming a second system of record you have to secure. **What happens if a state is not supported?** The call returns a structured 422 that names exactly what is missing for that jurisdiction. It never returns a zero. A silent zero is a wrong paycheck nobody notices until the filing, so the engine refuses to produce one. In a batch run, that employee comes back in an errors list with the reason and the rest of the run still calculates. GET /v1/jurisdictions reports the status of every jurisdiction before you rely on one. **Can it calculate a gross-up?** Yes. POST /v1/gross-up answers "give them $1,000 in hand — what does that cost in gross?" by running the full engine rather than dividing by an assumed rate, so wage-base caps and bracket edges are respected instead of averaged away. **Can it tell me whether a pay stub is legal?** POST /v1/statement/validate takes a pay statement and returns what the employee's jurisdiction requires on a wage statement, which of those items you already supply, and which are still missing. Every paycheck response also carries the required items alongside the numbers. **Does it file or remit taxes?** No. It calculates and returns numbers; it transmits nothing to any tax authority and holds no money. Filing and remittance stay with you or your provider. ### Sales Tax Finder — product page **What does the Sales Tax Finder API do?** Accurate US sales tax rates by address or ZIP. **How do I authenticate with the Sales Tax Finder API?** Send your key as a bearer token: `Authorization: Bearer `. One key works across every Geoverio API your plan includes. **Is there a free tier?** Yes. Every plan reaches every API; the free tier differs on monthly volume and rate limit, not on which endpoints you may call. ### Blog index **What data does Geoverio provide?** The site offers guides on IP, address and tax data for developers working with geolocation and sales tax rates. **How accurate is the payroll API?** It handles US gross-to-net calculations instantly for any employee address in a single call. **Where can I find more information?** You can read practical guides and honest deep dives written by the Geoverio team on the blog. ### Five places payroll math goes quietly wrong **Why do payroll calculators return wrong paychecks?** Because a wrong paycheck looks exactly like a right one. The five common causes are: calculating a pay period without the year-to-date history that decides where the Social Security wage base and the Additional Medicare threshold fall; returning a zero for a jurisdiction the engine does not actually support; selecting tax tables by calendar year when the jurisdiction changed them mid-year; treating every pre-tax deduction the same when states disagree about them; and producing correct numbers on a pay statement that is missing items the state legally requires. **Why does year-to-date matter for a single paycheck?** Two of the largest federal figures are annual rather than per-paycheck. Social Security stops once year-to-date wages reach the wage base, and Additional Medicare starts once they pass a threshold. Both boundaries are crossed in the middle of a pay period, so whether a given paycheck is correct depends on every paycheck before it in the year. **Is a pre-tax deduction pre-tax in every state?** No. Pennsylvania taxes 401(k), 403(b), 457(b) and SIMPLE IRA contributions at the state level even though they reduce federal taxable wages, while Section 125 contributions are excluded from both. California excludes Section 125 and 401(k) but taxes HSA contributions that are excluded federally. An engine with a single "pre-tax" flag per deduction gets both wrong, in the direction that under-withholds. **Can a pay stub be correct and still be illegal?** Yes. No federal statute prescribes what a pay statement must contain, but most states do and they differ. A stub can be arithmetically perfect and still omit an item the employee's state requires on it, which is a compliance failure rather than a math error. ### How multi-state payroll APIs automate tax compliance **How do I update tax rates without manual effort?** The system updates rates automatically based on official filings when you make a request with an address or ZIP code. **What happens if a user moves to a new state like Texas?** The next API request reflects the zero income tax environment in that state immediately without needing database updates on your end. **Is this useful for distributed work teams?** Yes, it keeps payroll simple regardless of where users live by calculating take-home pay using federal taxes and specific state deductions. ### What an IP geolocation API can and can't tell you **Can I use this for US sales tax?** Yes, the platform includes a Sales Tax Finder module that provides accurate US sales tax rates by address or ZIP. **Does the API detect VPNs?** The IP Lookup module detects VPN exits, open proxies, and Tor relays to help identify anonymized traffic. ### Documentation home **Do I need to install software to use Geoverio?** No prior experience is required. You create a free account by picking an email and password, then grab your API key from the dashboard before making your first call. **Where should I store my API key?** Keep the key in your server environment variables. Never paste it in public chats, screenshots, or code you push to GitHub to keep it secret. **Can AI assistants parse error responses correctly?** Yes. An expired key returns a 401 status with a valid JSON envelope. You must check the status before parsing the body to handle errors properly. ### Address Autocomplete — docs **What does the Address Autocomplete API do?** Fast, typo-tolerant US address autocomplete. **How do I authenticate with the Address Autocomplete API?** Send your key as a bearer token: `Authorization: Bearer `. One key works across every Geoverio API your plan includes. **Is there a free tier?** Yes. Every plan reaches every API; the free tier differs on monthly volume and rate limit, not on which endpoints you may call. ### IP Lookup — docs **What does the IP Lookup API do?** Geolocation, network owner, VPN/proxy/Tor detection and a risk score for any IP. **How do I authenticate with the IP Lookup API?** Send your key as a bearer token: `Authorization: Bearer `. One key works across every Geoverio API your plan includes. **Is there a free tier?** Yes. Every plan reaches every API; the free tier differs on monthly volume and rate limit, not on which endpoints you may call. ### Payroll API — reference **How do I authenticate with the Geoverio Payroll API?** Send your key as a bearer token: Authorization: Bearer . There is no other auth scheme — no query-string keys, no signing, no OAuth — and the same key works across every Geoverio API your plan includes. **How do I check whether a state is supported before I build on it?** Call GET /v1/jurisdictions. It returns every jurisdiction with its status — supported, partial or unsupported — the withholding method used, the official source behind it, and the local jurisdictions covered. **Where do the tax rates and limits come from?** GET /v1/limits/{year} returns the year's Social Security wage base, Medicare and Additional Medicare thresholds, FUTA rates and credit reductions, supplemental rates, and the 401(k), HSA, FSA and commuter limits. Behind them, every figure in the engine has a manifest entry recording its source URL, retrieval date and verification method. **Can I calculate employer cost as well as employee withholding?** Yes. POST /v1/employer-cost returns the employer side: Social Security and Medicare matches, FUTA including any state credit reduction, and SUTA from a rate you supply — state unemployment rates are assigned per employer and are not public data. ### Sales Tax Finder — docs **What does the Sales Tax Finder API do?** Accurate US sales tax rates by address or ZIP. **How do I authenticate with the Sales Tax Finder API?** Send your key as a bearer token: `Authorization: Bearer `. One key works across every Geoverio API your plan includes. **Is there a free tier?** Yes. Every plan reaches every API; the free tier differs on monthly volume and rate limit, not on which endpoints you may call. ### About Geoverio **Do I need a credit card to start?** No, the free tier requires no credit card and provides enough volume to build something real. **How do I handle integration changes?** You add another module without changing your integration, requiring no new credential or client. **Which certifications does Geoverio currently hold?** The Security page explicitly tells you which certifications are not yet held so you know exactly what you are buying. ### Changelog **Is there a free tier for Geoverio APIs?** Geoverio offers an optional free tier alongside predictable pricing and an available Enterprise tier. **How do I get started with the platform?** Developers can access all modules using one API key and read documentation that includes copyable code samples. **What security features does Geoverio provide?** Account security includes two-factor authentication by authenticator app or emailed one-time code, along with hashed recovery codes. ### Talk to sales **Does Geoverio require a credit card for the free tier?** No, the free tier needs no credit card and lets you make authenticated calls in minutes. **How quickly do sales teams reply to inquiries?** The team replies within one business day after receiving details on APIs and volume. ### Data Processing Addendum **What is the role of Geoverio in data processing?** Geoverio acts as the processor while you serve as the controller when sending personal data. **When must Geoverio notify about a breach?** They will notify within 72 hours of becoming aware of a personal data breach. **Do I need to sign anything to use these APIs?** Accepting these terms is required, but a signature is only needed if your procurement process demands one. **Are special category data supported?** No special category data should be sent to these endpoints as none are supported. ### Privacy Policy **Does Geoverio sell personal data?** No. We do not sell personal data, nor do we use API request content to build advertising or profiling products. **How long are sign-in attempt records kept?** We record the IP address of sign-in attempts for two days before automatically deleting them. **Who is responsible for API request content?** When you send us API requests, you act as the controller for that content while we serve as the processor. ### Security **How are passwords secured at Geoverio?** Passwords are hashed with Argon2id and a server-side pepper, meaning a database copy alone does not yield credentials. Two-factor authentication is available via authenticator app or emailed one-time code. **What happens if I find a security vulnerability?** Report it to the provided email address with enough detail to reproduce the issue. We acknowledge within two business days and will keep you updated until it is resolved. **Is card data processed on Geoverio servers?** No, card data never touches our servers as payments are handled entirely by Stripe, which is PCI DSS Level 1 certified. This keeps the API out of scope for PCI DSS. ### Terms of Service **How do I secure my API keys?** You must keep keys on your server in an environment variable, never in client-side code or a public repository. If a key is exposed, revoke it in your dashboard immediately. **What happens if I exceed my minute limit?** Requests beyond your per-minute limit receive a 429 response and are not billed. Rejected authentication and validation errors also do not consume quota. **Does Geoverio refund partial periods?** The service generally does not refund partial periods after cancellation, though you can write to them if the service materially failed to work as described. ### Pricing **How much does Geoverio cost?** See the plan table on this page for the current monthly prices. Every plan includes every API. **Is there a free plan?** Yes — a free tier with a monthly request quota, available once per account.