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://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=irto 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=pathsfor every observed AS path with peer counts,?view=graphfor the nodes and edges as drawn,?view=rpkifor the full origin-validation result including the ROAs that were considered, or?view=rawfor 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=darkfor 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://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://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.