BGPMap

For machines

HTTP API

Everything the pages show is available as JSON over plain GET requests. No key, no signup, and CORS is open so you can call it straight from a browser.

Endpoints

All paths are relative to https://www.bgpmap.ir.

GET /api/v1
Index of every endpoint, machine-readable.
GET /api/v1/query?q={input}
Run a lookup. Accepts an IPv4 or IPv6 address, a CIDR prefix, an AS number, or a hostname. Add &src=ir to keep only RIS peers registered in Iran. Returns a snapshot, or a list of targets when the input expands to more than one.
POST /api/v1/query
The same lookup with a JSON body: {"input": "8.8.8.8"}.
GET /api/v1/classify?q={input}
Report how an input parses and which address family it belongs to, without touching the upstream API. Useful for validating input before spending a request.
GET /api/v1/snapshot/{id}
A stored snapshot. Add ?view=paths for every observed AS path with peer counts, ?view=graph for the nodes and edges as drawn, ?view=rpki for the full origin-validation result including the ROAs that were considered, or ?view=raw for the untouched upstream response.
GET /api/v1/list/{id}
A stored list of targets. Supports ?p=, ?per_page= (max 1000), and ?f= to filter by substring.
GET /s/{id}.svg
The AS-path graph as SVG. Add ?theme=dark for the dark palette. Responses carry an ETag, so conditional requests are cheap.
GET /api/v1/sources
Which data sources this instance reads, and what they cover.
GET /health
Liveness, PHP version, and storage counters.

A first request

A lookup for an address returns a snapshot: the origin AS, the Most Observed AS-PATH, peer and collector counts, the RPKI state, and URLs for the citable page and the graph.

curl -s 'https://www.bgpmap.ir/api/v1/query?q=2606:4700:4700::1111'

An AS number or a hostname usually expands to many targets, so the answer is a list instead. Pick an item and query it to get a snapshot.

curl -s 'https://www.bgpmap.ir/api/v1/query?q=AS13335'

IPv6

IPv6 is a first-class input everywhere: addresses, CIDR prefixes, and AAAA records all work, and every response reports its address_family. Addresses are normalized before use, so 2606:4700:4700:0:0:0:0:1111 and 2606:4700:4700::1111 are treated as the same query. If you ask about a prefix longer than what is actually announced — a /64 carved out of a routed /48, say — the lookup falls back to the covering prefix and says so in quality_flags rather than reporting nothing.

Errors

Failures share one shape, so a client can branch on error.code without parsing prose:

{
  "error": {
    "code": "invalid_query",
    "message": "Enter a valid IPv4/IPv6 address, CIDR prefix, AS number, or hostname.",
    "status": 422
  }
}

Codes in use: invalid_query, query_too_long, invalid_id, not_found, wrong_kind, unknown_view, method_not_allowed, rate_limited, and upstream_failed.

Rate limits

Lookups are limited to 30 per minute per address, because each one costs an upstream request. Every lookup response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset; a rejected one returns 429 with Retry-After. Reading a stored snapshot or list is not limited, so fetch a result once and cite it as often as you like.

Caching

Answers are cached briefly upstream-side: routing data for a minute, AS names for far longer. Repeating a query inside that window is served from disk and returns in milliseconds. Snapshots never change once written, so they are safe to cache forever on your side.

Origin validation

The rpki_state in a snapshot is one of valid, invalid_asn, invalid_length, not_found, or unknown, and comes from the RIPE NCC RPKI Validator. Every snapshot carries the exact rpki_verify_url that was queried, and ?view=rpki returns the full result including the ROAs that matched, named a different AS, or allowed only a shorter prefix.

Routing data: RIPE RIS, retrieved through RIPEstat. Origin validation: RIPE NCC RPKI Validator. See Methodology for what the numbers mean.