API Documentation
meowtrace is a REST API for IP address → geolocation. Send an IPv4 address, get back country, region and city. All requests are JSON over HTTPS.
Base URL: https://api.meowtrace.com
Introduction
Every lookup returns a compact JSON record. v1 resolves to country, region and city — honest about resolution, with no invented precision.
x-credits-remaining response header, and a per-request id in x-request-id.Authentication
Lookup endpoints require an API key. Create one in the portal (shown once). Send it as a header:
x-api-key: mt_live_xxxxxxxxxxxxxxxxxxxxxxxx
Portal/account endpoints (keys, billing) use a bearer session token from sign-in instead: Authorization: Bearer <token>.
Errors
Errors share one shape: { "error": { "code", "message" } }.
| Code | HTTP | Meaning |
|---|---|---|
invalid_ip | 400 | Malformed IPv4 address |
bad_request | 400 | Malformed body or unknown route |
batch_too_large | 400 | Bulk batch over the 1000-IP cap |
unauthorized | 401 | Missing or invalid API key / session |
not_found | 404 | Valid IP, no containing range |
out_of_credits | 429 | Credit hard-cap reached — refill at next reset |
Single lookup
Resolve one IPv4 address. Costs 1 credit on success.
curl https://api.meowtrace.com/v1/lookup/1.2.3.4 \
-H "x-api-key: mt_live_…"
200 OK
{
"ip": "1.2.3.4",
"result": {
"ip": "1.2.3.4",
"country": { "code": "PL", "name": "Poland" },
"region": "Mazowieckie",
"city": "Warsaw"
}
}
Returns 404 not_found for a valid IP outside coverage, 400 invalid_ip for a malformed address.
Bulk lookup
Resolve up to 1000 IPs in one request. One bad entry never fails the batch — each result carries its own status.
curl -X POST https://api.meowtrace.com/v1/lookup \
-H "x-api-key: mt_live_…" \
-H "content-type: application/json" \
-d '{"ips": ["1.2.3.4", "8.8.8.8", "bad.ip"]}'
200 when all resolve; 207 Multi-Status when any entry is not-found, invalid, or unbillable:
{
"count": 3,
"partial": true,
"credits_charged": 1,
"results": [
{ "status": "found", "result": { "ip": "1.2.3.4", "country": { "code": "PL", "name": "Poland" }, "region": "Mazowieckie", "city": "Warsaw" } },
{ "ip": "8.8.8.8", "status": "not_found" },
{ "ip": "bad.ip", "status": "invalid" }
]
}
Per-IP status: found, not_found, invalid, or insufficient_credit. The batch is processed until credits are exhausted; the remainder are marked insufficient_credit.
API keys
Manage keys with a bearer session token. Keys are hashed at rest and the raw secret is shown only once.
| Endpoint | Body / Result |
|---|---|
POST /v1/keys | { "label"? } → { id, key, prefix, warning } — key shown once |
GET /v1/keys | { keys: [{ id, key_prefix, label, created_at, revoked_at }] } |
DELETE /v1/keys/:id | { revoked: true, id } |
Credits & tiers
1 credit = 1 lookup. Balance is hard-capped — when it hits zero, lookups return 429 until the next refill/reset.
| Tier | Price | Credits | Reset |
|---|---|---|---|
| Free | $0 | 10 / day | Daily, 00:00 GMT+8 (no rollover) |
| Hobby | $14.90/mo | 200,000 / mo | Monthly (no rollover) |
| Enterprise | $250/mo | Unlimited | Fair-usage policy |
Billing
Subscriptions and invoicing run through Stripe. From the portal:
checkout (body { "tier": "hobby" | "enterprise" }) returns a hosted Stripe Checkout URL. portal returns a Stripe Customer Portal URL for self-serve payment methods and invoices. A successful payment provisions your tier's credits automatically.