# Import IP99's OpenAPI Once: Postman, Codegen, and GPT Actions

> Source: <https://dev.to/ip99/import-ip99s-openapi-once-postman-codegen-and-gpt-actions-53a7>
> Published: 2026-10-31 13:08:29+00:00

Most IP-risk vendors hand you a PDF and a support ticket. IP99 by ThreatHunter publishes an **OpenAPI 3.1** document at a stable URL you can import the same way you import any other public API: Postman, Insomnia, `openapi-generator`, or a GPT Action. One file describes the free profile, the quota endpoint, and the professional Pulse path — so your clients, types, and agent tools stay in sync with the live service instead of a blog post you copied six months ago.

This is a practical import walkthrough. Every curl below runs **without an API key**. Live samples were captured on **2026-10-31** (UTC).

```
curl -s https://ip99.com/openapi.json | head -c 200
# → {"openapi":"3.1.0","info":{"title":"IP99 IP risk and geolocation API",...
```

As of today the document advertises OpenAPI **3.1.0**, title *IP99 IP risk and geolocation API*, version **1.0.0**, server `https://ip99.com`, and three paths:

| Path | What it is | 
|---|---|
| `GET /v1/ip/{ip}` | Free risk + geo + network profile (key optional) | 
| `GET /v1/me` | Your current tier and remaining quota | 
| `GET /pulse/v1/ip/{ip}` | Same score with per-evidence detail (professional key) | 

`Access-Control-Allow-Origin: *` is set on the OpenAPI JSON itself, so browser-based importers and GPT Action validators can fetch it without a CORS proxy. Security schemes in the file are exactly what the human docs say: `X-API-Key` header or `Authorization: Bearer`, **never** a query string (`?key=` is rejected with HTTP 400 `key_in_query`).

The free endpoint is still the one you should try first:

```
curl https://ip99.com/v1/ip/8.8.8.8
```

Captured ~2026-10-31 13:05 UTC:

```
{
  "ip": "8.8.8.8",
  "computed_at": "2026-10-31T13:05:33Z",
  "evidence_state": "none",
  "geo": {
    "continent": "NA",
    "country": "US",
    "region": "California",
    "city": "Mountain View",
    "district": "",
    "tz": "America/Los_Angeles"
  },
  "network": { "asn": 15169, "usage_type": "IDC" },
  "risk": {
    "score": 0,
    "level": "none",
    "signals": ["hosting"]
  }
}
```

`hosting` here is a **network attribute**, not a risk verdict. Score `0` with `evidence_state: none` means "no current verifiable evidence," **not** "safe." Official copy is blunt about that; your generated client should expose `evidence_state` as a first-class field, not bury it under "extra."

`https://ip99.com/openapi.json`.` lookupIP` (`GET /v1/ip/{ip}`), set `ip` to `8.8.8.8`, send. No Authorization needed for anonymous quota.` apiKey` and map it to the `bearer` scheme when you create a free key in the console. Fields do Anonymous allowance today (from `GET /v1/me`): **500 lookups/day** per client IP, **60/min**, resetting at midnight Asia/Shanghai. Sign in and self-create a key for a higher daily bucket; overseas self-serve Pro / Pro+ buys **volume**, not a different score. Per-item evidence rows, `share_tier`, and `?at=` history sit on Pulse and need a professional key.

Any OpenAPI 3.1 generator that understands 3.1 will work. Example with the official CLI (install once, then pin the URL):

```
npx @openapitools/openapi-generator-cli generate \
  -i https://ip99.com/openapi.json \
  -g typescript-fetch \
  -o ./ip99-client
```

Or Python:

```
openapi-generator-cli generate \
  -i https://ip99.com/openapi.json \
  -g python \
  -o ./ip99-py
```

What you should assert in a smoke test after generation:

`evidence_state` enum: `active` | `stale` | `none`.` risk.score` is documented (and treated in app code) as `risk.signals` is an array of slugs; `hosting` / `mobile` describe the network and are not, by themselves, a ban reason.`{ "error": { "code", "message" } }` — branch on `error.code`, never on the prose in `message`.
A second live call, using IP99's documented demo address (mask the last octet of **real** customer IPs in tickets):

```
curl https://ip99.com/v1/ip/27.22.66.200
```

Same window:

```
{
  "ip": "27.22.66.200",
  "computed_at": "2026-10-31T13:05:57Z",
  "evidence_state": "active",
  "geo": {
    "country": "CN",
    "region": "Hubei",
    "city": "Xiangyang",
    "district": "Yicheng",
    "tz": "Asia/Shanghai"
  },
  "network": { "asn": 4134, "usage_type": "DYN" },
  "risk": {
    "score": 7,
    "level": "low",
    "signals": ["dialup_pool", "hijacked_proxy", "proxy"]
  }
}
```

Residential `usage_type: DYN` plus proxy-class signals is exactly why "ban all IDC ASN ranges" fails: the interesting exits often sit on home broadband. Official triage order: read **`evidence_state` first**, then signals, then score/level — and prefer step-up verification over hard-banning a whole building when the address may be shared.

Custom GPTs / Actions that accept an OpenAPI URL can point at the same file. Practical checklist:

`X-API-Key`.`/v1/me`) unless the agent truly has a professional Pulse key. Leaving Pulse in the Action without a key only produces `403 professional_required` noise.`computed_at` alongside the score. A stale or empty evidence state with score 0 must not be summarized as "clean IP."
If you want agents without a custom GPT, IP99 also exposes the same free profile as an MCP tool `ip_risk_lookup` at `https://ip99.com/mcp` — same fields as `/v1/ip/{ip}`. That path is covered in an earlier post; the OpenAPI file remains the right source of truth for HTTP clients and codegen.

Every successful lookup can carry daily quota headers (`X-Daily-Limit`, `X-Daily-Remaining`, `X-Daily-Reset`). Prefer those (or `GET /v1/me`) over hard-coding numbers from an old article — allowances can change, and the response is authoritative for *your* tier.

Do not cache risk verdicts across sessions: the product thesis is that every answer is a **judgement that expires**. `computed_at` is part of what makes a decision auditable. Pulse's `?at=` replay exists for chargeback-style "what did we know then?" questions; the free `/v1/ip` endpoint answers **now** only.

Also out of scope for this import guide (and for honest marketing): inventing customer logos, accuracy percentages, funding claims, or equating IP99's free profile with ThreatHunter's full enterprise datasets. IP99 is a ThreatHunter product; the free JSON you get from `/v1/ip` is exactly what the OpenAPI schema for that path describes — nothing more.

`/v1/ip/8.8.8.8` with no key.
Human reference: [https://ip99.com/api](https://ip99.com/api). Product home: [https://ip99.com](https://ip99.com).
