Connect an AI assistant to BGPScout's internet-routing intelligence: search ASNs/IXPs/facilities/CDNs/traceroutes, look up IPs & prefixes, find sales prospects, and take account actions.
https://bgpscout.io/mcp → authorize (OAuth).
https://bgpscout.io/mcp (spec at /mcp/openapi.json), Auth = Bearer with an API key from /account (or header {"X-API-Key":"KEY"}).
list_tools from your assistant to enumerate everything on demand.| list_tools | Discover all available BGPScout tools, grouped by category, with a one-line summary each. Call this first to see the full capability set (search, lookup, prospecting, intelligence, account actions). Pass category to get one group with full descriptions, or detail: true for every full description. |
| lookup_asn | Look up detailed information about an ASN, including its PoP/facility count and IXP count. Location caveat: the record's latitude/longitude are the centroid of the registered POSTAL CODE, never the street address; the `location` field gives the resolved point with its provenance and precision, which may be a geocoded address, a data-centre facility, an exchange, or a country centroid. Do not report an approximate position as an address. |
| search_asns | Search ASNs with rich filters (name/asn, country, state, city, rir, traffic level, IPv6, on-IX, MANRS, RPKI, network type, asn_type flag, CDN provider, min/max PoPs/IXPs/peers/downstreams). PEERS CAVEAT: peer/downstream bounds and their sorts read counts of observed BGP AS_PATH adjacencies, which is NOT a peer list - it conflates transit, settlement-free peering and sibling relationships. Counts come from a curated multi-collector RouteViews + RIPE RIS build; get_asn_neighbours returns the exact collector set and build date in observed_from. They are FLOORS, not censuses, and more collectors does not lift that ceiling much: a RIB holds the best path per prefix per collector peer, so peering that is never the best path anywhere (the normal case for IXP route-server peering) is invisible to every collector. An ASN peering at a busy route server typically shows only a handful of observed adjacencies here, far fewer than its real peering count. Do not state a peer count as a fact without that caveat. An ASN with no observed adjacency at all is UNOBSERVED, not zero - it is silently excluded from min/max peer and downstream filters rather than being treated as zero, and both sorts place unobserved ASNs last regardless of direction. Downstream direction means observed nearer-to-origin AS_PATH adjacency; it is not proof of a commercial customer relationship. |
| bulk_lookup_asns | Summarize several ASNs at once (name, country, traffic, PoPs, IXP counts and domestic share) in one call. IXP fields are null with an ixp_data note where the ASN has no PeeringDB record - null means not surveyed, never zero exchanges |
| compare_asns | Compare multiple ASNs side by side - PoPs, IXPs, prefixes, routing quality, announced IPv4 address total, BGP downstream count and the owning registry organisation. Same columns as the /compare page. CAVEATS: `ipv4_addresses` is the merged union of announced space (more-specifics collapsed), and null means the network announces no IPv4 - it is not zero. `bgp_downstream_networks` comes from AS_PATH adjacency, which does NOT label relationships: an adjacency may be transit OR peering, and it is not proven hierarchy. It is a FLOOR, not a census - a RIB holds only the best path per prefix per collector peer, so adjacencies that are never anyone's best path (typical of settlement-free IXP peering) are missing regardless of collector count - and it is null, not 0, when this ASN was never observed adjacent to anything by any configured collector. `org` is resolved through the RIR allocation record, not asn.org_id. `org.org_url` is the canonical browser link for that organisation, null when it could not be resolved; it replaces the old `org.url`, which was the /orgs/{registry}/{org_key} fallback form rather than the canonical slug. `boomerang_rate` and `crossing_rate` are DIFFERENT measurements and are returned separately: crossing_rate is traces that left the country, boomerang_rate is traces that left and came back. `pop_count` and `ixp_count` are null, never 0, when PeeringDB has no record or an empty one, with the reason in `pop_count_note` / `ixp_count_note`. Every ASN you ask for comes back in the order you asked, and one we hold no row for carries an `error` field rather than being dropped. |
| get_asn_history | Historical trends for an ASN: measured facility/IXP counts, total IXP capacity and prefix counts over time, plus facility_ixp_archive - earlier facility/IXP counts reconstructed from recorded PeeringDB join/leave events, labelled derived and kept separate from the measured series |
| get_route_history | Historical view of an ASN's observed adjacency set (monthly, since 2001-10-26) or a prefix's origin-set changes (daily, since the series started - no backfill). THREE states throughout and they must not be collapsed: `gap` (no observation covering that period - we did not look), observed-absent (we looked and it was not there), observed-present. A gap is NOT a withdrawal. Prefix history before `series_start` is `unknown`, never 'not announced'. AS_PATH change points are NOT available: raw MRT is not retained, so `path_history` returns not_available rather than an empty array. |
| get_bgp_state | Current visible origin-set for an ASN, a prefix or an IP address, from the platform's nightly internal snapshot - NOT a live query (this platform has no live BGP feed access). Every response carries a `snapshot` block stating freshness (fresh/stale/unavailable) and provenance, read at request time. MOAS-aware: a prefix's `origins` array is never reduced to one row. `path_observation` is always `not_available` (raw AS_PATH is never retained). `at` (an ISO date) replays a prefix or IP's origin-set history to that date and is NOT supported for an ASN resource (returns a structured refusal, not approximated current data). `limit`/`cursor` paginate an ASN's prefix list. |
| get_asn_neighbours | Every ASN observed adjacent to this one in BGP AS_PATHs, in BOTH directions - this is the tool for "who does X peer with". CAVEATS: AS_PATH does NOT label relationships, so an adjacency may be transit OR settlement-free peering OR a sibling; it is not proof of a commercial arrangement. Direction is "seen nearer/further from the origin", an observation and not proven hierarchy. Built from a curated multi-collector RouteViews + RIPE RIS set; the response's observed_from field names the exact collectors and the build date, so do not describe the coverage from memory. A sparse result is mostly a statement about collector visibility, not about the network, and it stays that way at any collector count: a RIB carries only the best path per prefix per collector peer, so IXP route-server peering is largely invisible here. For networks that merely share an exchange - opportunity rather than observation - use get_asn_ixp_colocated instead. |
| get_asn_ixp_colocated | Networks present on at least one of the same internet exchanges as this ASN, from PeeringDB membership, ranked by shared exchanges. NOT A PEER LIST: co-presence on a fabric is a peering OPPORTUNITY, and two networks can share an exchange and exchange no traffic. Covers only ASNs with a PeeringDB record and a listed port. For neighbours actually observed in BGP, use get_asn_neighbours. |
| get_asn_peers | DEPRECATED ALIAS for get_asn_ixp_colocated - despite the name it returns IXP co-presence, not peers. Prefer get_asn_neighbours for observed BGP adjacency, or get_asn_ixp_colocated for shared exchanges. |
| get_asn_facilities | List the data-center facilities/PoPs a network (ASN) is present in, with a total count. Use this to answer 'how many PoPs/facilities does ASN X have'. Sourced from PeeringDB, which is SELF-REPORTED. `pop_count` is null, never 0, when the network has no PeeringDB record or has one that lists no facilities, and `pop_count_note` says which - large carriers routinely leave the section empty (AS3356 Lumen is the reference case). A null here means not disclosed; it never means the network has no PoPs. PAGING: `pop_count` is the FULL disclosed total and is never affected by `filter` or paging; `matched_count` is how many facilities match the current `filter` (or equals `pop_count` when no filter is given); `returned` is how many are in THIS page, and `truncated`/`next_offset` say whether more remain - a page is never passed off as a total. Default limit 200 - AS6939 has 339 facilities, past the point of a useful single reply, so page through with `limit`/`offset`. FILTER: `filter` is a free-text, case- and diacritic-insensitive, multi-token AND search over name/address/city/state/country - country accepts a full name ("Canada") as well as the ISO code ("CA") - the exact matching the /asns/:asn web page's Interconnection Facilities panel runs server-side, so this tool and that page cannot disagree about which facilities are Canadian. `filter_applied` echoes what was searched back in the response so a caller relaying a filtered count (e.g. "16 Canadian exchanges") cannot drop the qualifier. |
| get_asn_ixps | List the Internet Exchange Points (IXPs) an ASN peers at, with port speeds and total interconnection capacity. One row per DISTINCT exchange, with its individual ports nested under `ports` - a network commonly has several connections to the same exchange, so `ixp_count` (exchanges) and `port_count` (connections) are different numbers and `ixp_count` is the one that matches lookup_asn. `total_capacity_gbps` sums ports. `ixp_count` is null, never 0, when PeeringDB has no record or an empty one, with the reason in `ixp_count_note`. Counts only ports PeeringDB flags operational=true by default; pass include_non_operational to include down ports. PAGING: `ixp_count` is the FULL disclosed total and is never affected by `filter` or paging; `matched_count` is how many exchanges match the current `filter` (or equals `ixp_count` when no filter is given); `returned` is how many are in THIS page, and `truncated`/`next_offset` say whether more remain - a page is never passed off as a total. Default limit 200 - AS6939 is on 335 exchanges, which used to come back as a single ~112KB reply with no way to narrow it. FILTER: `filter` is a free-text, case- and diacritic-insensitive, multi-token AND search over exchange name/city/country (country accepts a full name like "Canada" as well as the ISO code "CA") and each exchange's peering-LAN IPv4/IPv6 addresses - the exact matching the /asns/:asn web page's Exchange Points panel runs server-side, so `filter: "Canada"` here returns the same 16-of-335 set the page does. `filter_applied` echoes what was searched back in the response so a caller relaying a filtered count cannot drop the qualifier. |
| get_asn_cdn_caches | List the CDN edge caches hosted inside an ASN, grouped by provider with cache locations. Providers known to have caches in this ASN but with no located node here are reported separately in providers_present_without_locations. total_caches counts LOCATED nodes only, so an ASN whose caches are all unlocated reports 0 there and names the providers in that list; do not read total_caches: 0 as 'no caches'. |
| get_transit_providers | An ASN's upstreams from two sources: traceroute-observed handoffs to the curated Tier-1 backbone list (narrow, ~490 ASNs, but carries the handoff city) and BGP AS_PATH adjacency (far wider coverage, no location - the response's bgp_upstreams note gives the real shape rather than a number that would go stale). AS_PATH does not label relationships, so a BGP adjacency may be transit or peering. |
| find_ddos_protected | Networks observed sitting behind a blanket DDoS scrubbing provider (Akamai Prolexic, Cloudflare Magic Transit, Global Secure Layer, Path, Imperva). Their routes were seen transiting a provider whose product IS the BGP announcement, so the service was live in the routing snapshot - this is evidence, not a contract, and absence does not mean unprotected. Distinct from the 'ddosm' tag, which means a network SELLS mitigation. Set exclusive=true for networks whose only upstreams are scrubbers. |
| get_asn_prefixes | Paginate IPv4/IPv6 prefixes observed with an ASN as origin. Snapshot counts are true totals, not the size of this page. Missing or inconsistent coverage is explicit; private/reserved ASNs are labelled as anomalous collector observations, not registered networks. |
| get_prefix_counts | Get IPv4/IPv6 prefix counts with history |
| get_asn_ipv6_reachability | Evidence that a network's IPv6 actually WORKS, not just that it announces v6 space. Returns the strongest level observed and every supporting observation with its date and vantage point. Levels, weakest to strongest: announced (announces v6 prefixes - NOT reachability evidence) < ixp_port (v6 configured on an IXP port, PeeringDB self-report) < aaaa (a name of theirs resolves AAAA) < path (a router in their own v6 space answered a traceroute) < icmp6 (their own space answered our ping) < tcp6 (a TCP handshake completed into their own space). An unconfirmed result means we have not observed it working - most ASNs have never been probed - and NEVER means the network has no IPv6. |
| get_recommended_ixps | IXPs this ASN should consider joining: exchanges seen in its traceroute paths, filtered to ones it can actually reach - a facility this ASN already has a presence in (tier 'facility'), or a city it already has a facility in (tier 'metro'). A candidate that clears neither is DROPPED, not ranked lower, however often it appears in a traceroute; traceroute-hop frequency is only a tie-break WITHIN a tier. An empty result with a non-zero raw_candidate_count means real candidates existed but none were physically reachable - a real finding, not missing data. Same ranking function as the ASN detail page's Recommended IXPs panel. |
| get_recommended_peers | Networks this ASN should consider peering with: candidates ranked by shared-infrastructure tier (shares a facility > already on a shared exchange > reachable via an IXP from get_recommended_ixps), with PeeringDB's disclosed peering policy (open/selective/restrictive/no, plus whether a balanced traffic ratio or a signed contract is required) as a secondary ranking signal and traceroute-hop frequency only as the final tie-break. A candidate clearing no tier is DROPPED, not ranked lower. peering_policy is null (not 'Restrictive') when PeeringDB has no disclosed answer - never treat null as a refusal. Same ranking function as the ASN detail page's Recommended Peers panel. |
| recommend_transit | Rank candidate transit providers from the curated Tier-1 backbone list by MEASURED round-trip latency from a source city to named destinations (Amazon, Google, Cloudflare, a named ISP, a literal ASN, an IP address, or a hostname) - answers 'from here, what transit gets me the best path to X'. RTT comes from the last RESPONDING hop of each matching traceroute (never from geolocating the destination IP - anycast destinations like Cloudflare/Google/Amazon geolocate to a head office, not to the site that actually answered, so this tool never reads destination geography at all). A candidate only appears under `ranked` for a target once it clears an evidence floor (min_observations distinct traces from min_sources distinct source ASNs, each with a usable RTT) - below that it is listed under `insufficient_evidence` with the exact shortfall, never silently ranked on a thin sample. `no_data` lists candidates with zero matching traces at all - absence here means 'not measured', not 'no good path'. `buyer_reachable` is set from your org's declared facility footprint (get_seller_profile) when you belong to one, else null - it is a cross-connect availability signal, not part of the ranking. An IP or hostname target is resolved LIVE (DNS + currently-announced covering BGP prefix, up to 10 per call, run in parallel) rather than from a corpus, so the resolved network is current as of the call - each such target's `resolution` field says how and when. This is evidence for a buying decision, not the decision itself: the platform holds no pricing, contract terms or port availability. |
| lookup | Everything we know about one IPv4 address, IPv6 address, CIDR or hostname, in a single call. Type is detected - no separate tool per input shape - and IPv6 is first-class. sections filters the payload: routing (covering prefix, origin ASN, more-specifics, bogon status - IANA special-use/reserved targets like documentation ranges resolve immediately), geo (resolved location with EVERY candidate source and which one won), registration (RIR allocation, registered org with a method/confidence/evidence field - null with a reason rather than an unrelated org when the registry's opaque id is ambiguous across resource types - and contacts), rpki (Valid/Invalid/NotFound against the ROA set), rdns (PTR for an address; for a prefix, `forward_domains` is one row per registrable domain with a subdomain count and a `likely_wildcard` flag, so one wildcard record cannot crowd out every other domain and the payload stays small. The per-hostname `forward_records` list is OPT-IN via include_forward_records and is absent by default; `forward_records_included` says which you got), blocklists (public blocklist status: per-list Listed / Not listed / Could not check, with the matching listings, the date each was first seen and the list's delisting page; lists that name the whole origin network are reported separately under network_listings; for a single address also live DNS-list answers and a live reverse-DNS check with forward confirmation; context lists such as Tor exits never count as a listing; `domains` says whether a looked-up hostname or its parent is on a public phishing list and, for an address, how many listed hostnames pointed to it when last resolved, with shared website builders and CDN/cloud addresses flagged). Every section carries its own source and observation date so you can say how confident you are rather than presenting a guess as fact. Prefer this over lookup_ip and lookup_prefix, which answer narrower slices of the same question. |
| lookup_ip | Geolocate an IP address (city/country/coordinates) and, when available, the ASN that announces it. `bogon` is set (and `geo`/`asn` are null) for IANA special-use/reserved space such as documentation ranges or RFC 1918 - these resolve immediately rather than paying for a routing-table scan guaranteed to miss. |
| lookup_prefix | For an IPv4 CIDR (e.g. 8.8.8.0/24), the ASN that announces the covering prefix. `bogon` is set (and `announced_by` is null) for IANA special-use/reserved ranges - these resolve immediately rather than scanning the routing table for an announcement that cannot exist. |
| get_asn_bogons | Bogon prefixes an ASN announces - IANA special-use space (RFC 1918, loopback, documentation, multicast, …) or space no RIR has delegated. Omit asn to list the ASNs announcing the most bogons. severity: high (special-use) | medium (undelegated) | low (registry holds it reserved; advisory only) |
| get_asn_rpki_status | RPKI route-origin validation summary for one ASN, from the bulk validation of announced_prefix against rpki_roa. Four states: valid, invalid, notfound (the majority state - no ROA covers the prefix, NOT a failure), unavailable (could not be evaluated). Never presents notfound as invalid. Not incident detection - no baseline, no history, just the current verdict per prefix. See get_rpki_validation_history for the change-over-time series built on top of this. |
| validate_announcement | Validates ONE EXACT (prefix, origin ASN) pair - the property RPKI actually has, never a prefix alone. Works for ANY pair, announced or not: it evaluates against the loaded VRP set directly, so it can answer "what would happen if AS64496 originated 203.0.113.0/24" where announced_prefix_rpki (rows only for currently-announced prefixes) cannot. RPKI states: Valid | InvalidASN | InvalidLength | NotFound | Unavailable. Unavailable (no VRP set loaded) is NOT NotFound (a real VRP set was consulted and nothing covered the prefix) -. Splitting Invalid into ASN/Length is a labelling convention, not RFC 6811: this checks ASN before length, so a covering VRP with a different origin reads InvalidASN even when the length would also fail, matching this platform's own bulk validator; RIPEstat labels that same case invalid_length. Read wrong_asn_vrp_count / too_long_vrp_count for the unambiguous facts. Verified 400/400 against the nightly validation run across all five outcome classes. NEVER call an RPKI-invalid announcement a hijack - it is usually a stale or misconfigured ROA. `checks` defaults to ["rpki","visibility"]; add "irr" to make live registry queries (slower), "rir" returns an explicit not-evaluated with the reason. visibility comes from the nightly internal snapshot, never a live BGP query, and "not observed" is not "not announced". |
| get_rpki_validation_history | RPKI validation-state TRANSITIONS - when a prefix's verdict (valid/invalid/notfound/unavailable) or invalid REASON (wrong_asn/too_long/both) genuinely differed between two consecutive nightly runs of the bulk RPKI validator. This is a change-series over ROA/configuration drift, and it is explicitly NOT incident detection: the validator samples once per ~24h, so `detected_at` on every transition is when the DIFFERENCE was first noticed, never when the underlying ROA or announcement actually changed - the real change could have happened, and fully resolved, at any point inside that window. An event that both starts and ends between two nightly samples produces ZERO rows here, structurally, forever; that is the sampling rate, not a bug, and an empty `transitions` list must never be read as 'nothing happened' without also checking `latest_comparison_run` (see below). Never call a transition a hijack or an incident: on our own corpus, an RPKI-invalid announcement is usually a stale or misconfigured ROA, not an attack, and a transition INTO invalid inherits that same caveat. `latest_comparison_run.has_baseline` is a FIRST-CLASS FACT, distinct from an empty result: has_baseline=0 means no prior observation existed for ANY prefix when that run executed (a from-cold-start seed, e.g. the very first run) - genuinely different from has_baseline=1 with transitions_found=0, which means a real comparison ran and found nothing to report. Very few comparison runs exist so far - the cold start with has_baseline=0, and quiet comparisons with has_baseline=1 and transitions_found=0 - and both kinds will legitimately return an empty `transitions` array for any filter - that is correct given only one nightly validation run has happened, not evidence this tool is broken. Keyed on (asn, cidr), never announced_prefix_id - that id is reassigned nightly by a rebuild-and-swap and is not stable across runs (verified: 1,392,531 distinct (asn,cidr) pairs for 1,392,531 announced_prefix rows). Pass asn alone for every transition touching that ASN's prefixes; add cidr for one exact prefix (and current_state is then also returned, the LATEST known baseline verdict for that pair); pass neither for the global recent-transitions feed. state/reason filter on the NEW (destination) state/reason of the transition. |
| get_rpki_comparison_runs | Audit trail of validation runs - one row per run, always exactly one regardless of how many prefixes exist, so this answers 'has the RPKI change-detection job actually been running' independent of whether it found anything. Each row's `has_baseline` is the absent-versus-zero distinction get_rpki_validation_history depends on: 0 means no prior observation existed for ANY prefix when THAT run executed (first-run/cold-start), 1 means a real prior baseline existed and `transitions_found` is a genuine comparison result (0 there means 'compared, found nothing', not 'did not compare'). `prefixes_new`/`prefixes_vanished`/`prefixes_compared` describe that run's (asn,cidr) coverage against the prior baseline. This is diagnostic/meta - most callers asking about a specific prefix or ASN want get_rpki_validation_history instead. |
| get_routing_incidents | List evidence-backed routing incidents for an ASN or prefix, with severity, confirmation state, observation dates, baseline facts and observer counts. Daily observations may miss changes between samples. Multiple origins and invalid RPKI states do not establish an attack; adjacent ASNs do not establish commercial relationships. Results include assessment availability and checks without comparable observations, so an empty list is not mistaken for a clean assessment. Defaults to open and observing incidents; use state all for resolved and suppressed findings. Pagination uses next_cursor. |
| get_route_security_status | Routing-security assessment for one ASN or prefix: observed prefixes and origins, RPKI validation results, observer coverage, comparison baselines and incident counts. RPKI validity applies to each prefix and origin pair. NotFound means no covering ROA; unavailable or uncollected results are separate. Publishing ROAs does not establish that a network filters invalid routes. Multiple origins alone do not establish an incident. IRR authorization is available separately through compare_bgp_irr_rpki. Includes assessment availability so missing results are distinguishable from a completed check. |
| get_irr_objects | Look up IRR/RPSL objects (route, route6, aut-num, as-set) directly from the public IRR registries - RADB, RIPE, APNIC, ARIN, LEVEL3, NTTCOM - live, on demand (short-TTL cache; not a bulk mirror). Give ONE of: `prefix` (route/route6 exact-match), `asn` (aut-num plus an inverse origin lookup across route/route6), or `object_name` (an as-set/route-set/aut-num/maintainer primary key, e.g. "AS-EXAMPLE"). EVERY requested source is queried INDEPENDENTLY and every object it returns is kept separate - two sources both holding the same name are NEVER merged, because IRR data is genuinely contradictory across registries (measured live: RADB and RIPE hold two different "AS-GOOGLE" as-set objects with different members). `source_status` reports what happened for EACH requested source on EACH sub-query - `ok` (answered with data), `not_found` (answered "no entries", a real positive answer), `error` (answered but the reply was rejected), or `unreachable` (did not answer at all, e.g. connect timeout) - these are different facts and are never collapsed into one; do not read a `not_found` on one source as evidence the object doesn't exist anywhere, and do not read a source missing from `objects` as "empty" without checking whether it was actually `unreachable`. Each returned object carries its raw RPSL (`raw_rpsl`), parsed `attributes` (each value is an array - RPSL attributes like `members`/`mnt-by`/`remarks` legitimately repeat), the object's OWN `source:` attribute (`source_attr`) alongside which registry you asked (`source`), and `source_authority` flagging any mismatch between the two. `history` is accepted but NOT supported in this build (no NRTM-fed mirror exists) - passing it returns an explicit `history_note` rather than silently ignoring it or fabricating history; the response is always the object as-of right now. `matched_count`/`returned`/`truncated` bound a large result (an inverse origin lookup on a big network can be thousands of route objects) - `cursor` is always null, since a single whois query answers in one response with nothing to page yet. |
| expand_as_set | Recursively expand an AS-SET (or route-set) into every member ASN, from the public IRR registries, with a HARD depth limit, a hard member-count limit, and cycle detection - real as-sets nest, and some are self-referential. FAIL-CLOSED: `complete` is false, with an explicit `incompleteness_reasons` list, whenever the depth limit truncated a branch, the member limit was reached (further ASNs are NOT added once hit - never silently capped and called done), a nested set could not be resolved from any requested source, or a source query errored/was unreachable. A caller MUST check `complete` before treating `asns` as authoritative for anything filter-shaped - an incomplete expansion that looks like a normal-sized result is exactly the case that black-holes legitimate traffic if used to build a prefix filter (this build does not generate filters). A CYCLE is reported in `cycles` but, once detected, is NOT itself a reason for `complete: false` - the traversal simply does not re-descend into an ancestor, which is the correct, complete answer for a self-referential set (verified against a synthetic 3-cycle fixture; confirmed live against real registries that none of the as-sets sampled while building this - AS-EXAMPLE, AS-HURRICANE, AS-NTT, AS-GTTCUST, AS-TELIANET/AS1299 - contain one, so a live-observed cycle example does not exist yet in this dataset). MEASURED LIVE 2026-08-25: real customer-cone as-sets are large - GTT's AS-GTTCUST alone has 500+ direct member ASNs AND ~766 nested per-peer sets in ONE object; Arelion/Twelve99's AS-TELIANET reached 2,436 further nested sets just three levels down. Expanding either FULLY would run for a projected tens of minutes to hours even at a good-citizen one-query-per-second-per-host pace - this tool bounds the work instead of attempting it, and `query_count` on every response tells you exactly how many registry queries THIS call made. `sources` is a PRECEDENCE list, not a fan-out: for each nested set, the first source in the list that has the object is used (mirrors the whois `-s SOURCE1,SOURCE2` convention) - this tool does not query every source for every node, unlike get_irr_objects, which does. `address_family` is accepted but informational only in this build (as-set membership is not family-specific in RPSL); see `address_family_note` in the response. |
| compare_bgp_irr_rpki | Puts FOUR independent evidence classes side by side for an ASN or prefix: what is actually ANNOUNCED (announced_prefix, real BGP RIB data), what IRR SAYS (live route/route6 objects via the same registries get_irr_objects queries), what RPKI SAYS (announced_prefix_rpki's bulk nightly validation, or an ad hoc rpki_roa coverage check when the prefix is not currently announced), and who the RIR DELEGATED FILES SAY the space belongs to (rir_allocation/rir_org - `matching` / `sibling` / `unrelated` / `unknown` relative to the registry organisation holding the origin AS). The product is the disagreement matrix: `matrix.all_agree` (the three-class bucket: announced, IRR origin match, RPKI valid), `matrix.announced_no_irr` (on the wire, no registered route object), `matrix.irr_not_announced` (registered in IRR, not seen on the wire), `matrix.rpki_invalid` (announced and RPKI-invalid) - each a `{count, prefixes}` list - plus `matrix.rir_matching` / `rir_sibling` / `rir_unrelated` / `rir_unknown` and `matrix.all_four_agree`, which carry `{count, cidrs}` (cidrs only; the full per-prefix evidence is always in `prefixes[].rir`). NONE of these buckets is mutually exclusive by design (an announced prefix can be BOTH missing from IRR AND rpki-invalid at once - that is two real facts, not a contradiction). Every row in `prefixes[]` keeps all four verdicts independently (`announced`, `irr`, `rpki_state`, `rir`) rather than collapsing them. NEVER CALL A MISMATCH A HIJACK: a prefix announced by an AS other than its allocation holder is usually a legitimate delegation, transfer or customer announcement, and `rir_unrelated` means only 'these two registry records do not identify the same organisation to us' - `rir_coverage.interpretation_note` restates this on every response. The RIR org resolution goes through the same corroborated resolver get_asn_allocations and lookup_rir_org use, and a `sibling` verdict is a byte-identical registered name under a DIFFERENT org id - a disclosure carrying `confidence: 'low'`, never a merge. Give ONE of `asn` (samples its announced prefixes, bounded by `sample_size` - default 200, max 2000; `announced_sample.sample_truncated` says if the sample is partial; the IRR side is a SINGLE inverse-origin query per source regardless of ASN size, same as get_irr_objects) or `prefix` (exact-match on both sides; if the prefix is not currently announced, RPKI falls back to an ad hoc rpki_roa coverage check, flagged in `rpki_coverage`). The absent-versus-zero rule applies throughout: `irr_coverage.query_complete` is false whenever a requested IRR source did not answer ok/not_found or the IRR result was truncated (`irr_coverage.incompleteness_reasons` says which); an announced prefix's `rpki_state` is `not_validated` when no row exists in announced_prefix_rpki (the nightly job has not scored it yet), which is NEVER the same fact as `notfound` (scored, no covering ROA exists); and `prefixes[].rir.status` has FOUR values that are never collapsed - `resolved` (a delegation record was read), `no_allocation_record` (checked, none covers this prefix), `lookup_failed` (the query failed, so it is unknown rather than absent) and `not_checked` (beyond `rir_sample_size`, unparseable, or `include_rir_allocation: false` - never queried). Do not treat a clean-looking matrix as complete without checking `announced_sample.sample_truncated`, `irr_coverage.query_complete` and `rir_coverage.sample_truncated`/`not_checked` first. |
| generate_prefix_filter | SAFETY-CRITICAL: generates a router-pasteable prefix list for an AS-SET or a single ASN, sourced from live IRR route/route6 objects. PREFERS REFUSING OVER GUESSING - `refused: true` with no `filter` field and explicit `refusal_reasons` whenever: the AS-SET expansion (expand_as_set) is not `complete`; the resolved member-ASN count exceeds `max_asns` (default 50, hard ceiling 100 - refuses rather than silently truncating the ASN list); ANY requested IRR source did not answer ok/not_found for ANY member ASN, or that ASN's route lookup was truncated (irrObjectLookup's own 500-object cap - a single very large network, e.g. a big transit provider, WILL refuse under this, measured live against AS13335); or the resulting prefix list would be empty (an empty filter is a deny-all on most routers) unless `allow_empty: true` is passed explicitly. A refusal still returns full `provenance` - which registries, what each answered, when - so a human can see exactly what was tried. On success, `filter.prefixes` is EXACTLY what IRR registers - no ge/le broadening to shorter/longer masks is applied (a router-vendor convention, e.g. bgpq4's default "up to /24", that this tool does not assume on your behalf; see `note_no_ge_le`). Default `sources` excludes LEVEL3 (measured chronically unreachable) so this tool is not refusing by default on a registry that never answers; pass it explicitly to include it, which also makes the call fail-closed on it like any other requested source. Every call, success or refusal, is logged to `prefix_filter_generation_run` (migration 191) for audit. `human_review_required: true` and `human_review_note` are always present, refused or not - THIS OUTPUT REQUIRES HUMAN REVIEW BEFORE USE ON A PRODUCTION ROUTER. |
| get_bgp_communities | BGP community reference: definitions grouped by well-known (RFC-registered), operator-specific, IXP/route-server-specific, and historical (out of validity window). Returns whatever has actually been collected for the subject: operator_specific and ixp_specific are populated where first-party documentation has been read, and empty where it has not. Call it rather than assuming - the `coverage` block in the response reports the live corpus size (well-known definitions, documented ASNs, documented exchanges). When you pass `asn` and nothing is on file, `operator_specific_note` says so in words: absence of documentation, never evidence a network has no community policy. Every definition carries its source_url/source_type/confidence and validity window - nothing here is inferred from the community's numbers. |
| decode_bgp_communities | Decode a list of BGP communities (standard "A:B", large "A:B:C" RFC 8092, or extended "rt:GA:LA"/"soo:GA:LA"/six raw hex bytes RFC 4360), one result per input, preserving order. Never invents a meaning from the numbers: each result is either a cited definition (source_url, source_type, confidence, validity window) or an explicit `status: 'unknown'`/`'invalid'` with a `reason` - most commonly "No documentation on file for AS<n>'s communities" when that operator has not been collected yet. `ambiguous: true` when multiple equally-authoritative definitions exist; never silently picks one. `context_match` says whether the community's own candidate-owner ASN agrees with `context_asn`, but the input is never rewritten by that context. Four-byte ASNs (large communities, and rt:/soo: with a Global Administrator > 65535, auto-selecting the RFC 5668 four-octet-AS encoding) round-trip exactly. |
| search_allocations | Search RIR address allocations - every IPv4/IPv6/ASN delegation the five registries published, with the date each block was handed to an organisation. Use for 'what has ARIN allocated recently', 'which blocks were assigned in <country> since <date>', or to date a prefix. Newest first by default. Each row carries org_key (the registry's opaque organisation id) and org_url, the canonical browser link for that organisation. org_url is null where the registry published no organisation id for the block, which means the holder is unknown to us and not that the org has no page; and today that is true of none of the rows this tool can return - but read the field rather than assuming, since it changes as registries publish. |
| get_asn_allocations | Paged IPv4/IPv6 blocks delegated to an ASN's registry organisation. Totals and transfer/direct summaries cover the complete inventory, not only the returned page. IPv4 and IPv6 have independent cursors so one family cannot crowd out the other. SCOPE: every total is for ONE registry org handle, and a company routinely holds several - AS16509 resolves to an ARIN handle with 5 IPv4 blocks while an identically-named sibling handle holds 1,807. When siblings exist, `related_orgs` names them with the extra block counts; walk them with lookup_rir_org's org_key. `org_resolution` is 'resolved', 'unresolved' (the ASN sits in a delegation range carrying no organisation id, so the holder is unknown - counts come back null, NOT zero) or 'absent' (no delegation record at all). |
| search_transfers | The RIR transfer logs: IPv4/IPv6 blocks and ASNs that changed hands between organisations, naming both parties. transfer_type distinguishes a market transfer (the block was bought or leased) from a merger/acquisition (the holding company itself was acquired). Use for 'who has been buying address space', 'where did this prefix come from', 'what has <company> sold', or 'which ASNs were acquired in <year>'. Covers ARIN, RIPE NCC, APNIC, LACNIC and AFRINIC. |
| lookup_rir_org | Registry organisations and their independently paged ASN, IPv4 and IPv6 inventories. Every family reports total, returned, truncated and next_cursor so large holders are never silently incomplete. SCOPE: a registry org handle is not a company. Totals are `scope: 'this registry org handle only'`; RIRs issue a company several handles over mergers and transfers, and 643 registered names in the corpus are carried by more than one org. `related_orgs` discloses the handles sharing this one's exact registered name plus the extra blocks they hold - disclosed, deliberately not merged, because a registry name string is not proof of common ownership. When an ASN matches no organisation, `org_resolution` separates 'unresolved' (a delegation range exists but carries no organisation id) from 'absent' (no delegation record at all). Also returns `ipv4_utilization`, the org's allocated/advertised/DORMANT IPv4 address counts - see search_dormant_ipv4's description for what 'dormant' measures and its caveats, which apply here too. |
| search_dormant_ipv4 | Find registry organisations holding significant unadvertised ("dormant") IPv4 space - the lease/transfer/sales prospecting signal built for the /orgs page, now reachable from MCP. WHAT 'DORMANT' MEASURES, EXACTLY: an address is dormant if it was NOT covered by any BGP route we observed in ANY of the last `window_days` nightly snapshots (7 by default). It is a MEASUREMENT, not a verdict - dormant is not the same as unused or available: the space can be internal-only, reserved, mid-migration, or announced solely to peers outside our DFZ view. Every result carries `nights_observed` (how many of those nights actually contributed - render this, not just window_days) and `measured_at`. ORG-LEVEL BY DESIGN, NOT ASN-LEVEL: the announcing ASN is irrelevant to whether an org's address is routed at all - summing per-ASN figures undercounts an org whenever its org-to-ASN linkage is incomplete, which made Amazon read as 20.8% dormant (an artifact of only 4 of its ASNs being linked) versus the correct org-level 84.8%. So this tool returns organisations, never ASNs; to ask 'how much dormant space is behind ASN X', call get_asn_allocations or lookup_rir_org for that ASN's org_key, which each carry the same ipv4_utilization figures attributed to the ORG. DISTINCT FROM get_org_asns's filter=dormant: that means an ASN never observed announcing IPv4 at all (a fact about the ASN), completely different from address-space dormancy (a fact about the org's allocated blocks) - an org can be 100% utilized and still hold a dormant ASN, or fully-announcing ASNs and still sit on a dormant /12. COVERAGE: every response carries `coverage.orgs_computed` vs `coverage.orgs_holding_ipv4` - an org the nightly job has not reached yet is NOT a 0%-dormant org, it is unmeasured, and cannot appear in these results at all until it is computed. `country` filters/returns rir_org.country, which is DERIVED FROM THE ORG'S OWN ASNs - it is not the registered country of any one block, and only resolves for orgs holding at least one named ASN. |
| get_dormancy_by_registry | Global IPv4 dormancy totals grouped by registry (ARIN/RIPE NCC/APNIC/LACNIC/AFRINIC) - the 'which RIR has the most dormant space' rollup, for a headline or a per-registry comparison rather than a per-org search (see search_dormant_ipv4 for that, and for what 'dormant' measures - the same 7-night-window definition and caveats apply here). Five rows, computed at request time from org_ipv4_utilization (already indexed on registry, no new collection). Each row reports orgs measured, allocated/advertised/dormant address totals and dormant-of-allocated percent. `coverage` states orgs measured vs orgs holding IPv4 GLOBALLY - a registry with a low measured-org count is understated in this rollup, not actually low-dormancy; check coverage before ranking registries against each other. |
| find_near | Find facilities, IXPs and CDN caches within a radius of a location (latitude/longitude in degrees, radius in km) |
| find_networks_near | Networks (ASNs) with IP address presence in an area - the geolocated IP footprint behind the IP-density heatmap. Use for 'which networks have IPs in/around <place>'. Ranked by number of geolocated IPs. Give either latitude+longitude (+radius_km) or a city and/or country. Coverage: ~6.5k ASNs with enough geolocated space to map; a network absent here may still operate there. |
| get_asn_density | The per-location IP-density breakdown behind the prospect map for one ASN. Requires Sales access. Locations are geolocated address-space observations, not registered offices or facilities. |
| get_asn_geo_prefixes | Which address space an ASN has in a place, at PREFIX granularity - the inverse of find_networks_near (which goes place -> ASNs). Give an asn alone for its full per-location prefix breakdown, or add city/country or latitude+longitude+radius_km to constrain it. Returns the announced CIDRs with the geolocated IP count of each, not just totals. Coverage differs by field: every located prefix has coordinates and ip_count, but a city/region/country label exists only where some network has been located in that grid cell, so an ASN can have located prefixes with NO city label and a city filter will not see those rows. The response carries the counts for the ASN you asked about; do not assume a coverage figure. An empty result means we have no geolocated space for that ASN, NEVER that the network is absent from the area. Coordinates are a 0.1-degree grid cell (~11km), not a precise centroid. Each row also carries an anycast verdict: is_anycast true means that prefix answers from many sites at once, so its coordinate is one sample, not a place the network is uniquely in - never report such a row as evidence of presence there; is_anycast null means unknown (a mixed-type ASN with no prefix-level anycast fact), not confirmed unicast. |
| find_transit_near | Networks (ASNs) observed taking transit handoffs in an area, and from which curated Tier-1 backbone, derived from traceroute data. Use for 'which ASNs have transit locations in/around <place>'. Give latitude+longitude (+radius_km) or a city/country. Default grain is one row per asn/transit_asn/city - an asn/transit pair seen handing off in five cities returns five rows, not one row summed across all of them. Pass aggregate_by: 'provider_pair' for a national rollup instead: one row per asn/transit_asn with `observations` summed, `city_count`, and a `cities` breakdown; `city`/`country` are only present on that row when city_count is 1. Coverage is narrow - only ~490 ASNs across 11 Tier-1 providers currently have observed handoffs, so absence is not evidence of absence. |
| find_upstreams_near | Networks (ASNs) present in an area together with their BGP upstreams. Use for 'which networks around <place> buy transit, and from whom', or with upstream_asn for 'who near <place> is behind <provider>'. Far wider coverage than find_transit_near, but the two halves come from different sources: location is PeeringDB facility presence, upstreams are BGP AS_PATH adjacency - an upstream listed here was NOT observed handing off in that city, and AS_PATH cannot distinguish transit from peering, and the coverage is a floor that does not grow much with more collectors. Use find_transit_near instead when the handoff location itself must be real. |
| find_networks_at_facilities_near | Networks (ASNs) with a PeeringDB data-centre/PoP presence in an area, and which facilities. Use for 'who is in a data centre in/around <place>' or to find cross-connect candidates in a metro. Give latitude+longitude (+radius_km) or a city/country. COVERAGE: the radius can only be tested against facilities that have stored coordinates - a facility with memberships but no lat/long is left out of `networks`, not because it is outside the radius but because it cannot be evaluated. The response's `coverage` field reports how many candidate facilities exist, how many were geocoded, and (when you passed city/country) the specific `unlocated_matches` omitted - treat those as unresolved, not absent. |
| find_networks_on_ixps_near | Networks (ASNs) peering at IXPs in an area, with port capacity. Use for 'who is reachable on an exchange in/around <place>' or to find peering candidates in a metro. IXPs are located via their facilities. Give latitude+longitude (+radius_km) or a city/country. COVERAGE: an IXP present only in un-geocoded facilities cannot be placed in the radius and is left out of `networks`, not ruled outside it. The response's `coverage` field reports the same facility/membership omission accounting as find_networks_at_facilities_near, and `unlocated_matches` when you passed city/country. |
| get_events | The change feed: ASNs newly registered, IP blocks newly allocated, resources transferred between organisations, networks joining/leaving IXPs, IXP port-speed changes, facility presence added/removed by a NETWORK, an EXCHANGE becoming present at or leaving a facility, a NETWORK gaining or losing a CDN cache, an EXCHANGE first appearing in or being delisted from PeeringDB, and a FACILITY doing the same . Filter by ASN, country, IXP, facility, event type and date range. Use for 'what changed for AS X', 'who joined IXPs in <country> recently', 'who bought address space recently', or 'what new networks appeared since <date>'. CAVEATS you must pass on: ixp_facility_added/ixp_facility_removed start EMPTY - ix_facility has no date column, so the history table took its first observation on 2026-07-27 and zero events means 'nothing has moved since we started looking', not 'no exchange has ever gained a facility'. cdn_cache_added/cdn_cache_removed start EMPTY for the same reason - cdn_cache_node is current state only, its first baseline was taken on 2026-07-27, and its granularity is the (ASN, programme) pair, so a cache farm growing from 3 nodes to 4 emits nothing. ixp_first_seen/ixp_delisted and facility_first_seen/facility_delisted are the EXCEPTION and do NOT start empty - they are derived from the monthly CAIDA PeeringDB archive and carry 2,347 events from 2010-08-01, though the series is monthly with three gaps so 111 delistings have a window up to five months wide. The facility pair adds 6,978 events on the same basis, derived from a first-seen/last-seen WINDOW rather than per-snapshot presence, which means it cannot express a gap and has no 'relisted' - measured at 6 disagreements in 1,965 when the same method is checked against exchanges. And ixp_speed_changed's 1,364 existing rows were all derived on 2026-07-25 from a membership table that has since been replaced; the current builder re-derives none of them. |
| find_growing_networks | Networks ranked by how fast they are GROWING or DECLINING - net new data-centre facility presence, new IXP presence, and new address space over a 30/90/365-day window. Combine with a location (latitude+longitude+radius_km, or city/country) to answer the sales question directly: 'which fast-growing networks have a presence where I can reach them'. Set direction=declining to find networks contracting. Caveat: IXP membership history only starts 2026-04, so IXP figures are not yet a true year-over-year comparison. |
| get_asn_growth | Growth/decline detail for one ASN: net facility and IXP presence change and new address space over 30/90/365 days, with current totals for context. |
| get_ixp_info | Search Internet Exchange Points by name/city/country/operator, with min_peers/max_peers/min_facilities filters - this IS the /ixps browse listing, not a lookup that requires already knowing the PeeringDB id. Each result carries its operator (the PeeringDB organisation that runs it - always present), where we could establish one the registry organisation behind it with the method and confidence that link rests on, and a `virtual` classification (score/tier/reasons) saying how likely the exchange is a virtual fabric rather than a physical one. `traffic_milestones` lists third-party claims that the exchange's own public traffic graph has peaked above a threshold (today the 1 TeraBit Club list, threshold_bps = 1e12), each with the graph URL and the list's as-of date; scope 'operator_aggregate' means the listed graph totals this exchange with the ids in aggregate_with_pdb_ix_ids, so it does not claim this exchange alone passes the threshold. An EMPTY list means the exchange is not on the list, not that its traffic is below the threshold. DEFAULTS: min_peers = 1, so exchanges with no membership recorded in PeeringDB are excluded - pass min_peers=0 to include them (they are often defunct rather than never-real, which is itself informative). exclude_virtual defaults to FALSE: virtual exchanges are returned, labelled, unless you ask for them to be dropped. The website's /ixps page applies the same min_peers default. operator_org.org_url is the canonical browser link for that registry organisation - the same page a person clicking it on the site lands on; it is null when the org's registry identity could not be resolved, and operator_org itself is null when no registry link was established at all. It replaces the old `url` field, which was hand-built as /orgs/{registry}/{org_key} - the documented fallback form, which resolves but is not the canonical link. |
| get_ixp_members | List the ASNs that peer at a given IXP (by its PeeringDB id from get_ixp_info). ONE ROW PER MEMBER, with that member's ports nested under it - a member with two ports is one row, not two. Each port carries its IPv4/IPv6 peering-LAN address and, where the operator declared one in PeeringDB, the facility (building) that port terminates in. A port with no facility is UNDECLARED, not "in no facility". PAGING: `member_count` is the FULL membership, `returned` is how many are in this page, and `truncated`/`next_offset` say whether there are more - a page is never passed off as the total. OPERATIONAL: members and capacity count only ports PeeringDB flags operational=true, so a member whose every port is down is absent; pass include_non_operational for the historical shape. |
| get_facility_info | Search data centers/facilities by name/country/city, with a count of how many ASNs are present - this IS the /facilities browse listing, not a lookup that requires already knowing the PeeringDB id. |
| get_facility_members | List the ASNs present in a data-center facility (by its PeeringDB id from get_facility_info). Returns an OBJECT, not a bare array: `members` plus `member_count` (the full total), `returned`, `limit`, `offset`, `truncated` and `next_offset`. It used to return a bare array that silently stopped at the limit, so a 721-ASN facility came back as 200 rows with nothing saying so. `member_count` now matches get_facility_info's asn_count exactly; a tenant we hold no ASN record for is returned with `asn: null` and its PeeringDB net id rather than being dropped. |
| get_facility_events | One building's change feed and the exchanges inside it - the same data as the /facilities/<id> page. Returns the facility record, the IXPs physically in it, the PeeringDB campus it belongs to (with its sibling facilities) and the carriers PeeringDB lists there, and the merged event log for the building and its exchanges, with counts so a capped feed is never mistaken for the total. `campus` and `carriers` each carry `loaded` and `as_of`: when loaded is false the list has not been read, which is NOT the same as no campus or no carriers. campus.campus null with loaded true means PeeringDB places the facility in no campus. Carriers are self-listed in PeeringDB, so an empty list is what PeeringDB shows, not proof that no carrier serves the building. A sibling with in_directory false is a PeeringDB facility id we hold no record for. Events reached via an exchange are labelled: an IXP join at a multi-site exchange is not a statement about this one building. |
| find_near | Find facilities, IXPs and CDN caches within a radius of a location (latitude/longitude in degrees, radius in km) |
| search_cdn_caches | Find CDN edge caches by country/provider/city. provider: ggc | oca | fna | akamai | aws_cloudfront | microsoft | apple. Node-level LOCATIONS exist only for ggc, oca, fna, akamai, aws_cloudfront; for the rest we know which ASNs host their caches but never collected node locations, so those providers return the hosting ASNs with location_data='not_collected' instead of an empty list. An empty result for them means WE DID NOT COLLECT IT, never that the caches do not exist. |
| get_peering_program_providers | Hyperscaler peering-program partner lists. program='google_vpp' (default): Google's Verified Peering Provider list - networks Google itself lists as having multiple private interconnects (PNIs) with Google, by metro (IATA code; multi_pop=true means PNIs to different Google PoPs in that metro), with tier (gold = metro redundancy, silver = PoP redundancy) and sales regions. Filter by metro, tier, region or provider name substring. `networks` is OUR match from the provider's website, not part of Google's list: network_match 'resolved' identifies the network; 'ambiguous' means several networks register the same website and Google does not say which holds the interconnects. ABSENCE MEANS NOTHING: the program is a voluntary, limited rollout updated by hand, so an unlisted network may still peer privately with Google. `checked` is the date we last read the list. lookup_asn carries the same listing for one network as `peering_programs`. program='azure_peering_service': Microsoft's Azure Peering Service partners (tier isp|ixp|partner), metros as 3-letter codes, from Microsoft Learn (CC BY 4.0). program='azure_expressroute': providers Microsoft lists as ExpressRoute connectivity partners, by peering location (metros[].places names the peering locations, e.g. Amsterdam, Amsterdam2; multi_pop=true means more than one peering location in that metro). program='google_partner_interconnect': service providers Google lists for Partner Interconnect, tier l2_l3|l2|l3 (the Layer 2 / Layer 3 service type Google prints), sales_regions are Google's geographic areas. For both, `networks` is OUR match from the provider's EXACT name (a reviewed name, or a PeeringDB network of that exact name registered in a country where the provider is listed): network_match 'resolved' identifies it, 'ambiguous' means several networks carry that exact name, 'country_mismatch' and 'no_match' mean no network is attached. program='google_dedicated_interconnect': a FACILITY list, not providers - the colocation locations where Google offers Dedicated Interconnect (one per edge availability domain, e.g. iad-zone1-1), returned as `facilities` with metro, facility_id (PeeringDB facility id, or null when Google names a building we could not match exactly), cloud_region and link_types (port speed in Gbps and MACsec support); tier and region do not apply. Microsoft Learn and Google Cloud documentation are CC BY 4.0: `attribution` carries the title, link and licence to show with the data. |
| search_traceroutes | Search traceroutes by source/dest ASN, country, destination host, platform, category, boomerang, detour country, severity, address family, or quality score. NOTE `hop_count` in these rows counts RESPONDING hops, not path length, and understates any path containing a timeout - get_traceroute returns path_length_hops for a real comparison. platform (ripe-atlas | linux) is the real collector/vantage-point filter - use it for "give me RIPE Atlas traces". category is a granular PER-TARGET label the collector assigns (a few hundred distinct values, e.g. "Cross-border - US to tranco_ca", "ISP - TELUS Communications AS852"), NOT a platform selector - category="RIPE Atlas" only matches the literal legacy label used before the collector switched to per-target labels in April 2026 and will look empty-ish for current data. category matches as a case-sensitive substring so a family like "Cross-border" or "TELUS" can be matched without knowing every exact label. Rows carry `left_source_country`, not the legacy Canada-only `leaves_canada` - a French or Brazilian domestic trace can have left_source_country=1 while never touching Canada. ipv6=true restricts to af=6 traces - the RIPE Atlas importer only started writing this flag once af=6 measurements existed, so a trace older than that has ipv6=0 regardless of its real address family; a colon in dest_host/dest_ip is the fallback tell for older rows. detour_country and severity filter on traceroute_boomerang_detail (migration 098) - only boomerang=1 traces ever have a row there, so passing either narrows to boomerang traces even without boomerang=true set explicitly. Rows also now carry detour_country/severity/detour_distance_km (all null on a non-boomerang row, or on a boomerang row the backfill has not yet reached). |
| get_traceroute | Get one traceroute with hop geolocation provenance and a first-class domestic, left_country or undetermined crossing verdict. Legacy booleans are null when the verdict is undetermined. On path length: `hop_count` counts only hops that REPLIED and is kept for compatibility - use `path_length_hops` (the last TTL reached) to compare path lengths and `responding_hops` / `missing_hops` to see how complete the path is. When boomerang=1, `boomerang_detail` carries WHERE the detour went (detour_countries, furthest_detour_country), detour_distance_km, severity_tier, severity_weight/gravity_index (a continuous 0-1 / 0-100 distance-weighted severity, same formula get_boomerang_stats aggregates - null, not 0, when detour_distance_km was not collected), destination_tier (top_1k/top_10k/unranked/null from Tranco rank - unranked means checked and not in the top 10,000, null means never checked, coverage is partial), latencyPenaltyMs (this trace's own RTT minus the median RTT of domestic, non-boomerang traces to the same destination, migration 176 - null with latencyPenaltyNotCollectedReason set to no_own_rtt/no_dest_host/insufficient_baseline/not_collected when it could not be measured; null is the COMMON case and never means 0 - most boomerang traces have no usable baseline, so read the reason field rather than assuming a penalty of zero) and fault attribution - null if this trace is not a boomerang or it has not been computed for this trace yet. Each hop carries `reply_data`, per-packet Atlas responder detail from traceroute_hop_reply (migration 106) alongside the hop's own primary-responder ip/rtt_min/avg/max. `reply_data.reply_data_state` is 'observed' (with `responders`: an array, primary first, each with its own rtt_min/avg/max and reply_indexes) or 'not_collected' (`responders`: null) - NEVER read a missing/null responders as "this hop had one responder": most traceroute_hop rows have NO per-packet data - it predates migration 106 for everything else, and for the buckets where one msm_id was stamped on many unrelated traces that history is unrecoverable. Do not guess the proportion from this text: every response carries `reply_data_coverage` counted from the hops it actually hands back, which cannot disagree with them. `reply_data.ecmp_divergence` is true only when more than one distinct responder ip was actually observed at that hop - distinct ADDRESSES, not merely several replies, which is a much larger and different set. Trace-level `reply_data_coverage` summarizes how many of this trace's hops have reply data at all. |
| get_my_traceroutes | Your own submitted traceroute batches, or the traces in one owned batch (pass batch_id). Unlike search_traceroutes, this reads your private submission corpus and always scopes by your user_id. category (only meaningful with batch_id, since a batch listing has no per-trace category) matches like search_traceroutes does: a case-sensitive substring against the per-target label (a few hundred distinct values), not an exact match - an exact "RIPE Atlas" only hits the legacy label used before the collector switched to per-target labels in April 2026. |
| get_asn_traces | Get traceroute data for an ASN |
| get_route_heatmap | Busiest city-to-city links observed in traceroute data (internet route heatmap). Pass from_city (and optionally from_country, to disambiguate a repeated city name) to answer a different question - not "what are the busiest links overall" but "where do paths go NEXT from this specific city" - the traceroutes.js /analysis/city-links gap this closes. from_city matches the merged/normalized spelling this tool already returns in from_city/to_city, not raw traceroute_hop.city. |
| get_country_scoreboard | Countries ranked by routing quality (boomerang rate, cross-border rate) [BETA: the underlying calculation is still settling - treat the result as directional, not yet safe to quote as a stated fact. See /traceroutes/analysis/country-leaderboard on the site for the current caveats.] |
| get_tier1_comparison | The 11 curated Tier-1 transit providers, ranked by BGP-wide downstream network count (RIB-derived, so the order does not depend on where our probes are). Reads the same durable connectivity_snapshot_run/connectivity_asn_metric/interconnect_provider snapshot as the /traceroutes/analysis/tier1 page rather than running its own live query, so this tool and that page cannot disagree about the same named comparison. transit_traces, avg_rtt, avg_hops etc are all frozen at the snapshot's own window - see the returned snapshot.window_start/window_end, not a rolling "as of now" figure. tier1_peers_observed and cloud_interconnects_observed are TIER1<->TIER1 and TIER1<->cloud/CDN handoff EVIDENCE from that same snapshot; the old customer_networks/handoff_cities/observations figures (pulled live from a separate asn_transit_handoffs table on its own 180-day window, never shown on the page at all) are retired by this migration - see notes.retired. Returns available:false with an empty providers list if no connectivity snapshot has completed yet. Traceroute counts are corpus floors, not traffic shares. |
| get_tier1_city_view | Curated Tier 1 x curated Tier 1 peering and curated Tier 1 x cloud handoffs actually observed in traceroutes through one city or metro area, over the snapshot's window - the server-side equivalent of the By City section on /traceroutes/analysis/tier1. Reads the same durable connectivity snapshot as get_tier1_comparison rather than a live query, so this tool and that page cannot disagree. A city with nothing here means this corpus has no probe traffic through it, NOT that no Tier 1 is present there - check get_tier1_comparison for what the corpus does cover. `city` is matched as a case-insensitive substring against the snapshot's own city names, same as the page's search box. |
| get_asn_connectivity_profile | Precomputed connectivity profile for ANY ASN, not just the curated Tier-1 eleven. Reads only a durable, versioned snapshot - the same connectivity_snapshot_run/connectivity_asn_metric/connectivity_observed_handoff/interconnect_provider tables get_tier1_comparison also reads - and never falls back to a live aggregate, so a stale or failed refresh is visible as staleness rather than silently substituted with a different computation. `measured` means this ASN was in the precomputed population for that snapshot with real observations; `unobserved` means it was not selected or had no evidence - absence is not zero, never report an unobserved ASN as having no connectivity. observed_handoffs are consecutive-ASN-transition EVIDENCE, not a commercial relationship claim (transit vs settlement-free peering vs sibling cannot be told apart from adjacency alone). This profile does not read asn_adjacency/PeeringDB directly, so it does not carry the collector geographic bias documented on get_asn_neighbours/#208 - but that also means it currently has NO population beyond whatever the curated seed list covers until the broader population and priority-score work lands. If available is false, no completed snapshot exists (or the requested snapshot_id does not); that is a fault on our side, not evidence about the ASN. Three further sections share this same snapshot, each with its own three-state `state` (measured / measured_none_observed / unobserved) and `note` exactly like the top-level fields above - measured_none_observed means this ASN WAS measured and simply had no such evidence in this window, never collapse that with unobserved (not in the precomputed population at all): `cdn_cloud_interconnects` (observed handoffs to a curated cloud/CDN interconnect_provider ASN, canonical multi-ASN operators like AWS/Google/Microsoft merged into one entry); `metro_path_diversity` (distinct metros with observed handoff evidence - metro BREADTH only, never a redundancy claim); and `observed_path_diversity` (per-handoff interface/IXP/temporal-path evidence from connectivity_handoff_interface/connectivity_handoff_path - labelled Observed Path Diversity, never Redundancy, since traceroutes cannot prove contractual, physical or operational redundancy. Two tiers kept separate: WEAK is distinct interface IPs, which may be two routers or two interfaces on ONE router since alias resolution is not collected; STRONG is an IXP actually observed on the crossing, sparse by measurement - only a small minority of handoffs with interface data have any IXP observed, so its absence is never evidence that no IXP was involved. Concurrency is REQUIRED, not assumed - a handoff's `paths.concurrent_overlapping_pairs` only counts path pairs whose own first_seen/last_seen windows actually overlap; a path used last year and a replacement used today do not count. connectivity_handoff_path is now fully built: 18,422 path rows covering all 5,927 handoffs, mean 3.1 paths per handoff, max 181. `paths.state: 'no_path_rows'` is still reachable and still means a snapshot-build gap, never a finding of 'one path' or 'no diversity'). |
| get_common_routes | Most frequently observed source-ASN → destination-ASN traffic flows [BETA: the underlying calculation is still settling - treat the result as directional, not yet safe to quote as a stated fact. See /traceroutes/analysis/routes on the site for the current caveats.] |
| get_boomerang_stats | Get boomerang routing stats for a country. Returns per-ASN rates (asns, each with an attribution breakdown - avoidable/conditional/structural/unknown - or null if not yet computed for that ASN), plus by_detour_country and by_severity (regional/intercontinental/extreme/not_collected) breakdowns for the whole country - a boomerang is where it went and how severe, not one blended rate. Gravity has both halves now: each ASN carries distance_gravity (avg_one_way_km, gravity_index 0-100, measured/unmeasured split) and destination_gravity (top_1k/top_10k/unranked/not_collected counts from Tranco rank - coverage partial, unranked != not_collected, popularity not traffic volume), plus country-wide distance_gravity_overall/destination_gravity_overall. See destination_gravity_note in the response for the coverage and popularity-vs-traffic caveats. [BETA: the underlying calculation is still settling - treat the result as directional, not yet safe to quote as a stated fact. See /traceroutes/analysis/boomerang on the site for the current caveats.] |
| get_asn_routing_quality | ASNs ranked by traceroute-observed routing quality (boomerang rate) among traces THEY SOURCED in the last 180 days - existing evidence, not a peering candidate suggestion (see get_recommended_peers for that). Same DOMESTIC_TRACE eligibility floor as get_country_scoreboard: only traces confirmed domestic-or-crossing count toward eligible_traces, never the raw total. classification is needs-more-data (below 200 eligible traces), poor-peering (>30% boomerang), moderate (>10%), or good - the SAME floor-then-threshold order the web page uses so colour/label cannot disagree. Admin only (access_level >= 300); the web page also accepts a personal beta_features flag below that level, which this tool does not replicate. |
| get_dashboard_summary | Platform-wide traceroute corpus stats - the same three cheap /dashboard widgets (stats, recent-traces, category-breakdown), combined into one call. NOT per-user: every signed-in caller sees the same numbers, same as the web widgets they mirror. `stats` is total_traces/boomerangs/unique_asns/countries across the whole approved corpus. `recent_traces` is the 15 most recently active dest_host+dest_country pairs (NOT per-trace or per-batch - grouped by destination, distinct from get_my_traceroutes/get_asn_traces). `category_breakdown` is traceroute counts grouped by target_category (uncategorized traces are labelled 'Uncategorized', not dropped). |
| find_co_located | Networks that share a data-center facility with a given ASN (cross-connect / peering candidates) |
| find_transit_customers | Networks observed DOWNSTREAM of a given transit ASN (reverse of get_transit_providers). Returns traceroute-observed handoffs (with cities) and BGP AS_PATH downstreams (far wider coverage, no location). NEITHER list proves a commercial relationship: both are routing observations, and a downstream position is equally consistent with paid transit, settlement-free peering, a sibling network, or transit supplied by an upstream on that network's behalf. Do not report these networks as customers of, or as buying transit from, the queried ASN - only as observed downstream of it. The tool name is retained for compatibility and overstates what the data shows. |
| get_org_asns | List all ASNs under the same organization. Pass an asn (finds its siblings via the registry allocation record, same identity the /orgs pages use) or an org_id (asn.org_id - a shorter, registry-specific handle like ARIN's 'MCICS', NOT the same identifier as the /orgs URL's opaque_id; used only as a fallback when no asn is given). Same sort/filter/pagination as the org detail page: sort (advertised|prefixes|peers|asn|name, default advertised), dir (asc|desc), filter (all|announcing|dormant - 'announcing' means observed announcing IPv4 in the DFZ, not merely delegated), page. NOTE: filter=dormant here means an ASN NEVER OBSERVED ANNOUNCING IPv4 - a fact about the ASN's activity, not about address space. It is unrelated to search_dormant_ipv4's dormant_addresses, which measures unadvertised IPv4 address space held by the org regardless of which (if any) ASN announces the rest. An org can be 100% address-utilized and still have a dormant ASN here, or have every ASN announcing and still hold a dormant /12 there. advertised_ipv4/prefix_count/peer_count are null (not zero) for an ASN never observed announcing or with no peer-count row - a registry delegation is not evidence of activity. org_url is the canonical browser link when the org's registry identity could be resolved, else null. |
| get_transit_handoff_locations | Where a network's routes enter its transit providers, as the PROVIDERS' OWN published BGP communities state it: in the latest routing-table snapshot, the provider tagged routes learned from this network both as customer routes and with a location community saying where they entered its network. `meaning` is the provider's own description of the location; `relationship_evidence` is the customer-route community. Pass asn (the customer network), provider_asn, or both. PROVIDER-ONLY (provider_asn alone) answers 'where does this provider receive its customers': `locations` lists every qualifying location tag with its distinct `customers` and `prefixes`, and `subjects_total` is the provider's distinct customers across all locations. Every count (`considered`, `candidates_total`, `subjects_total`, `locations`, `eliminated_by`, `would_fail`) is over ALL matching observations; only the `candidates` list is capped by `limit` (largest prefix count first) and `truncated` says when. Only providers whose documentation we have read and ruled on contribute; an empty result says nothing about the network, only that no such provider tag was seen. `eliminated_by`/`would_fail` explain every observation that did not qualify. Routing-plane evidence, not a statement about contracts or ports. |
| get_asn_relationships | A network's tier and its provider, customer and peer links, from the CAIDA AS Relationships Dataset (monthly; BGP plus multilateral peering, inferred with CAIDA's validated method). tier 1 = in CAIDA's inferred transit-free clique; 2 = sells transit (has customers); 3 = no customers. Counts are full totals; the lists are capped by `limit` and ranked by each neighbour's own customer count. status 'absent' means the network is not in CAIDA's snapshot - not tier 3. ixp_route_server: true when CAIDA lists the ASN as an Internet exchange route server; such a network has tier null and tier_label 'Internet exchange route server (per CAIDA)' and must not be described as a transit tier (null = that month's list was not read). customer_cone: the network itself plus every network reachable through provider-to-customer links at any depth (networks), and the IPv4 prefixes those networks announce (ipv4_prefixes, on ipv4_prefixes_date, null when unavailable); this recursive cone is larger than the cone CAIDA publishes on AS Rank. customer_cone null means not computed for the month, not zero. provider_changes: transit providers gained (added) and lost (removed) since previous_month; status 'not_compared' means there was no earlier month to compare against, which is NOT 'no changes'. `tier_label` names its basis ('Tier 1 per CAIDA'). `curated_tier1` is a different list, BGPScout's curated Tier 1 backbone list (the tier1 filter and badge elsewhere), and the two disagree for some networks: say which one you mean. Cite as `citation` when repeating this. |
| get_aspa | A network's own RPKI ASPA declaration: the transit providers it has signed as authorised to propagate its routes (as0 = true means it declares no upstream at all). Also how many other networks name it as their provider, and how the declaration compares with providers inferred from global routing data (inferred_not_declared = seen acting as a provider but missing from the declaration; routes via those may be dropped by networks that verify declarations). status 'absent' means no ASPA is published, which says nothing about its providers. Validated RPKI data, refreshed daily. |
| get_community_relationship_evidence | Which networks tag a network's routes with their OWN documented customer or peer BGP community, compared with CAIDA's inferred relationship for the same pair. A row means: in the latest routing-table snapshot, provider_asn put `community` on routes it learned directly from subject_asn (the next ASN on the path), and provider_asn's own documentation defines that community as marking a customer (or peer) route - `meaning` is its wording. Only reviewed customer/peer rulings count, only the tagger's own community in its own namespace, only definitions in force on the snapshot date; `excluded` counts every observation that failed one of those, by reason. `caida_relationship` is CAIDA's inference from the tagging network's side (provider = it is subject_asn's provider, customer = the reverse, peer, or null = no link inferred) and `agreement` is agrees / disagrees / no_caida_link / caida_not_loaded - a disagreement is reported, not resolved. Pass asn (the tagged network), provider_asn (the tagging network), or both. `totals` are over every qualifying observation; `rows` is capped by `limit`, largest prefix count first, with `truncated`. Routing-plane evidence of how a network labels a session, not a contract. Cite CAIDA as `caida_citation` when repeating its part. |
| find_prospects | Find sales-prospect networks by use case. use_case: transit | colocation | ixp | ixp_competitive | network_health. Same engine as the web prospecting tool. Dismissed/tagged leads are hidden by default, matching /prospect; set hide_dismissed=false to include them. target_ixp changes what use_case=ixp MEANS: without it you get networks on no domestic exchange at all, with it you get candidates for THAT exchange - anyone already peering there on a working port is excluded, and the rest are ranked by how many of the exchange's own facilities they are already present in, because a network in the building can be cross-connected and one three cities away cannot. An unknown target_ixp is an error, not a silent fallback. `country` (the PROSPECT network's registered country, e.g. "US" or "BR") is REQUIRED and has no default - it used to silently fall back to CA, so an omitted country now returns an error instead of a Canadian answer to a non-Canadian question. For use_case=transit specifically, `country` filters the prospect's own registry, not the probe location (it used to conflate the two and return networks from other countries); `source_country` filters where the traces were measured FROM, if you want that instead. crossing_rate is computed from left_source_country, the general column, not the legacy Canada-only leaves_canada - for CA that is several times more crossings than the legacy column counted, so rankings and the 10% inclusion threshold differ from before. |
| find_long_haul_customers | Networks with long-haul routes (potential transport/backbone customers) |
| find_transport | Networks observed carrying long-haul traffic between two regions (transport/backbone providers). Filter by from/to country or city. |
| get_cloud_connect_prospects | Networks that are candidates for cloud on-ramp / direct connect, best first. Ranked by prospect_score, then cloud_peering_score, then ASN - the cap is applied AFTER ranking, so a smaller limit returns the best N rather than an arbitrary N. |
| get_prospect_tags | Your saved prospect tags (customer/prospect/not_interested) on ASNs. A dismissed lead is stored as the not_interested tag, so this is also the review list for anything dismissed at /prospect. |
| get_clients | Your CRM clients/accounts and their member ASNs and IXPs - the list behind /prospect/clients. This is where a client_id for add_asn_to_client comes from. Scoped to your own clients plus your sales org's, exactly as the web page is. |
| get_search_templates | The seller profile, linked assets and target countries used to personalize /templates search recommendations. Requires access level 100, matching the page. |
| get_asn_lead_score | Sales lead score and opportunity signals for an ASN |
| get_lead_reasons | Why an ASN is flagged as a sales lead - opportunity type and signal breakdown |
| get_new_networks | Recently registered ASNs (new networks), optionally by country |
| get_growing_networks | Superseded by find_growing_networks, which it now calls: fastest-growing networks by facility/IXP expansion over 90 days. Use find_growing_networks for other windows, declining networks and location filters. |
| get_prefix_growth | Networks with the largest IPv4 prefix growth between the latest two completed RIB snapshots. Returns both snapshot dates. |
| get_country_benchmark | Per-country network/routing benchmark stats (network counts, IPv6 adoption, IX presence). ipv6_pct is measured-only and reproducible: it is ipv6_networks / ipv6_measured_asns, NOT over total_asns. ipv6_unknown_asns is the population we have never measured and it is excluded from both sides of the percentage rather than counted as "no IPv6". on_ix_pct works the same way and matters more: asn_on_ix is unknown for a large minority of networks, so networks_on_ix must NOT be read against total_asns - the response carries the known/unknown split. |
| get_top_networks | Top networks in a country ranked by a metric. by: traffic | prefixes | facilities | ixps |
| domain_lookup | Two separate evidence classes for a domain, never one guessed ASN. `website_associations` is a company's SELF-REPORTED website (PeeringDB/bgp.tools) - identity, not hosting. `dns_answers` is what actually resolves: observed A/AAAA records grouped by ASN from a completed domain intelligence scan, or a live DNS + routing-table resolution when the domain has never been scanned. `primary_network` is set only when dns_answers has a majority ASN, with a method/confidence/evidence field - it is null when the evidence does not support one, and it is NEVER taken from website_associations (Netflix owns AS55095 but is hosted on AWS; google.com's website row is a different ASN than the 173/174 DNS records observed for it). |
| search_domains | Search the domain→ASN index (which networks host which domains). `country` filters on the hosting country OBSERVED in scan records, the same figure the coverage page counts - not the hosting network's country of registration, which is returned separately as asn_country. Not every indexed domain has a scanned country, so absence from a country's results is not evidence the domain is not hosted there - the response reports what it actually matched. |
| get_domain_intel | Domain intelligence scan summary plus a deterministic page of records. Returns total_records, returned, truncated and next_cursor. Also returns vendors - third-party services the domain's own records name (verification TXT tokens, SPF includes, MX hosts, CNAME targets), each with its evidence rows; a verification token shows an account existed, mail and hostname records show current configuration. vendors is null, not an empty list, when the scan did not record those record types. Also returns dns_profile - nameserver/mail ASN, SPF provider and DMARC - when this domain has been profiled by the DNS sweep. dns_profile is null, NOT zeros, for a domain not yet profiled, and only a small share of the scanned corpus is: do not read null as "no nameservers". dns_profile_history_count says how many snapshots exist, because one is a baseline and cannot show a change. |
| compare_domain_scans | Compare two Domain Intelligence scans of the same domain (subdomains, IPs, ASNs, hosting type, country). Both scans must be visible to you - yours, your org's, or any if you are an admin; an id you cannot see and one that does not exist return the same error. Returns older/newer (whichever scan_id is actually more recent by created_at), plus added/removed/changed/unchanged record lists and totalOld/totalNew counts. |
| create_export | Start a reproducible asynchronous export job. Runs in the background - poll get_export(job_id) for progress; state starts "queued" then moves to "running" then a terminal state (completed/failed/cancelled). The response records the EXACT normalized filters, columns and source cutoff used, so the same call can be re-run later and compared - that is what "reproducible" means here. No arbitrary SQL: every dataset has a typed, allowlisted filter/column schema and an unknown filter or column is a validation error, not a silent no-op. The registered datasets include routing_incidents (daily routing findings with public evidence facts and assessment metadata); asns_search is the first one a web route serves from (POST /asns/exportSearch) rather than duplicating: "asns" (public ASN directory), "prefixes" (announced routes), "rpki_validation" (per-prefix RPKI verdicts, joined on (asn,cidr) so withdrawn prefixes are excluded), "ixp_members" (one row per member, not per port), "traceroutes" (approved public traces only), "facility_members" (one row per facility membership, never grouped by resolved ASN - that would silently collapse memberships whose ASN did not resolve), "allocations", "transfers", "events", and "asns_search" (the full 52-column ASN search export, admin-only at access level 300 - the same gate the web route enforces). Call list_exports for the exact filters and columns of each. Unknown filter KEYS are rejected rather than ignored: a typo such as "cuontry" fails the request instead of silently exporting everything and writing the typo into the reproducibility manifest. |
| get_export | Status and, once complete, the manifest and download link for an export job you created. Returns three DISTINCT outcomes, never collapsed: a running job has no row_count yet and is not a failure; a completed job with row_count=0 is a genuine empty result, not an error; a failed job has error_class/error_detail and is never reported as a zero-row success. download_url is present only when state="completed". It is a SHORT-LIVED SIGNED link (GET /mcp/exports/:id/download?exp=&sig=) valid for 15 minutes - download_url_expires_at says exactly when - and it is minted fresh on every get_export call rather than stored, so an old response holds an expired link, not a permanent one. The signature is bound to the specific artifact, so it stops working the moment that artifact is replaced or reaped. Possession of the link is enough to download it within that window; the same path also still accepts your normal API-key/OAuth auth with no token at all, which is the safer way to fetch it if your client can set a header. A job id you do not own and one that does not exist return the identical "Export job not found." |
| list_exports | Your own export job history (never another caller's - job visibility is scoped to the principal that created each job), newest first, paginated. |
| cancel_export | Cancel a queued or running export job you created. A job already in a terminal state (completed/failed/cancelled/expired) returns an explicit "already <state>" refusal rather than a silent no-op success. Cancellation here is LOCAL (this server's in-process runner checks for it between batches and cleans up the partial file) - there is no separate remote worker to signal, which the response's note field says explicitly. |
| create_measurement | Launch an on-demand ping, traceroute, or dns lookup from a Globalping vantage point. Returns immediately with state="queued" - this does NOT wait for results; poll get_measurement(job_id). Requests are NEVER deduplicated against each other or against scheduled coverage: every call creates a fresh measurement, on purpose, so an incident-response retry always gets a real new probe. Pass idempotency_key to make a RETRY of the same logical request safe instead - a second call with the same key returns the original request (dedup="reused_idempotency_key") rather than spending quota twice. Guardrails, all enforced before submission: for ping/traceroute, the target is resolved and rejected if it (or any of its resolved addresses) falls in a private/loopback/link-local/multicast/reserved/cloud-metadata range; for dns, the target (the domain QUERIED) is only shape-checked - it is deliberately NOT pre-resolved/blocked, because a name that legitimately NXDOMAINs or has no A/AAAA record is a normal, valid dns target, not an error - but dns_resolver (see below), a real network endpoint the probe connects to, gets the SAME private-range rejection target does for ping/traceroute; a per-caller cap of 20 probes per rolling hour and at most 3 concurrent in-progress requests, both independent of Globalping's own platform-wide free allowance (one caller cannot exhaust that allowance for everyone else); budget_mode is fixed to "free_only" (credit spending is not available yet - passing "credits_allowed" is refused). address_families defaults to [4]; passing [4,6] submits one job per family (paired v4/v6), sharing one job_id/request group. probes_per_selector is capped at 5. For type="dns": the result carries a `dns_outcome` per answer - "resolved" (NOERROR, with `answers`/`resolved_addresses`), "nxdomain" (a real completed answer, not an error), or "dns_error" (SERVFAIL/REFUSED/etc, code preserved) - alongside the usual transport-level `status`, which is what actually failing to get any DNS response (timeout, resolver unreachable) reports; "not yet run" is the job/request state, polled via get_measurement. source REQUIRES at least one of countries[0]/asns[0]/cities[0] (a single Globalping location selector; there is no "any probe worldwide" option in this slice) plus any number of tags to narrow it - selecting several DISCRETE locations in one call (e.g. "3 different ISPs") is not built in this slice. |
| get_measurement | Status and results for a measurement job you created. Three states, never collapsed: "running" (still in progress at the provider), a terminal state with usable_result_count=0 / zero_usable_results=true (the job ran to completion but nothing came back "finished" - a real distinct outcome, not an error and not silently treated as success), and "failed" (a real submission/provider error, in `error` and per-job `last_error`). "partial" appears when a paired v4/v6 request had one address family succeed and the other fail or get cancelled. Every job (one per requested address family) reports its own state/attempts/last_error alongside the request-level rollup, and every result row keeps the RAW provider payload (`raw`) as well as a normalized `summary` - never a single collapsed value, so multiple per-hop responders are never reported as loss. A job id you do not own and one that does not exist return the identical "Measurement job not found." |
| list_measurements | List the measurement jobs you created, newest first. Scoped to you: another user's jobs are never returned, and a non-admin passing someone else's user_id gets the same "Measurement job not found." that get_measurement uses, so this cannot probe which job ids exist. Filters: state, measurement_type, target (exact match, not a substring), created_after / created_before. limit defaults to 25, clamped to 100. Each row carries the same rolled-up lifecycle state get_measurement reports, from one shared function, so a list can never disagree with a detail view. |
| cancel_measurement | Cancel a measurement job you created. Local and provider outcomes are reported SEPARATELY and an upstream refusal is never reported as a successful cancel: a job not yet submitted to Globalping (still claimed in the ledger only) is genuinely cancelled and quota is never spent on it; a job already submitted cannot be cancelled - Globalping has no measurement-cancellation API - and the response says so explicitly (`provider_cancelled` is always false; `jobs_already_submitted_to_provider` counts what could not be stopped) rather than pretending the request went away. A job already in a terminal state (complete/failed/cancelled) returns that state with an explanatory note, not an error. |
| get_watchlist | Everything you are watching. Each row carries subject_type ('asn', 'ixp', 'facility' or 'org') and subject_id; `asn` is filled in only on an ASN row and is null otherwise, which means 'not about a network', not AS0. Add or remove: add_to_watchlist/remove_from_watchlist (ASN), add_ixp_to_watchlist/remove_ixp_from_watchlist (IXP), add_facility_to_watchlist/remove_facility_from_watchlist (facility), add_org_to_watchlist/remove_org_from_watchlist (registry org). |
| get_alerts | Your recent alerts/notifications |
| get_notifications | Your unread in-app notifications (the notification bell), newest first. Distinct from get_alerts, which is watchlist alerts specifically - this is a separate table with its own writer. Dismiss one or all with dismiss_notification. |
| get_saved_searches | Your saved searches |
| get_auto_scout | Your auto-scout (auto-run saved searches) with the latest result counts AND the identifiers that arrived or left since the previous run - so "3 new" can name the three |
| get_favorites | Your favorite ASNs. Add or remove with add_favorite/remove_favorite. |
| get_trace_favorites | Your favorited traceroutes (the star on a trace-detail page), newest favorite first. Add or remove with add_trace_favorite/remove_trace_favorite. This is a different favorites list from get_favorites, which is ASNs. |
| get_dashboard_pins | Cards pinned to your dashboard - the same list /dashboard renders, decorated with name, country and a reason to watch each one. |
| get_dashboard_digest | Your personal 7-day dashboard digest: facility/IXP count changes on YOUR watched ASNs, watchlist quality alerts, new traceroutes to/from YOUR watched ASNs, and new-match status of YOUR saved searches. Always scoped to the calling principal - never returns another user's watchlist or saved searches. watched_asn_count=0 means you have nothing on your watchlist, which is a real state, not a failure; the ASN/quality/trace sections are only populated when watched_asn_count > 0. |
| list_alert_destinations | Your alert delivery destinations. Never returns the webhook signing secret or a rotation's previous_secret - both are shown once, at creation/rotation time (create_alert_destination / update_alert_destination's rotate_secret), and never again after that. Each entry's enabled/verified/unsubscribed state and its type-specific target (webhookUrl for a webhook; emailAddress for email, though email cannot be created yet - see create_alert_destination) are included. |
| get_alert_deliveries | Bounded delivery history for your alert destinations: state (pending | sent | dead | cancelled), attempt count, and the most recent attempt's outcome class - "delivered" (2xx), "endpoint returned 500" (retryable_http/permanent_http, with the real HTTP status in response_code), "endpoint unreachable" (timeout/connection_error/ssrf_rejected, response_code null), or "never attempted" (no attempt row exists yet at all - a distinct, absence-based fourth state, not a zero or a guess). Never returns request bodies, response bodies or secrets. Filter by destination_id and/or state; bounded by limit (default 50, max 200). |
| list_watch_rule_deliveries | Which of your alert destinations one of your watch rules is opted into, and whether each opt-in is currently on. A rule does NOT deliver anywhere by existing - creating a rule and creating a destination are two separate acts and this is the third one that connects them. Returns destination_id, type, label, destination_enabled (the destination's own on/off switch) and rule_delivery_enabled (this rule's opt-in). BOTH must be true, and the destination must be verified, for an alert on this rule's subject to be queued. An empty list means this rule delivers nowhere - which is the default, not a fault. A watch_rule_id you do not own returns an empty list, exactly like one that does not exist. |
| get_seller_profile | Your sales org seller profile and its full colo footprint: facility-level service areas, metro/city-level service areas, and the buildings PeeringDB already places the org in as read-only suggestions. A footprint declared only by metro is a real footprint - do not read an empty service_areas as "none". |
| add_to_watchlist | Add an ASN to your watchlist. Idempotent: calling this again for an ASN you already watch UPDATES its notes/share_with_org rather than duplicating the row - that is also how to change them later. |
| remove_from_watchlist | Remove an ASN from your watchlist |
| add_ixp_to_watchlist | Follow an IXP - alerts on membership joins/leaves and port-speed changes. Same idempotent-update behaviour as add_to_watchlist: call again to change notes/share_with_org. |
| remove_ixp_from_watchlist | Unfollow an IXP. |
| add_facility_to_watchlist | Watch a facility (data center) - the same Watch button as a facility page. Alerts on membership changes at that building. Idempotent: calling again updates notes/share_with_org. |
| remove_facility_from_watchlist | Unwatch a facility. |
| add_org_to_watchlist | Watch a registry organisation (RIR org) - the same Watch button as an org page. org_key is the opaque_id from lookup_rir_org. Idempotent: calling again updates notes/share_with_org. |
| remove_org_from_watchlist | Unwatch a registry organisation. |
| pin_to_dashboard | Pin an item to your dashboard. type: asn | ixp | facility | org. |
| unpin_from_dashboard | Remove a pinned item from your dashboard. |
| add_favorite | Favorite an ASN - the same star button as the ASN page. |
| remove_favorite | Un-favorite an ASN. |
| add_trace_favorite | Favorite a traceroute - the same star as the trace-detail page. trace_id is the id from search_traceroutes/get_my_traceroutes, not a batch_id. |
| remove_trace_favorite | Un-favorite a traceroute. |
| report_data_issue | Report incorrect or missing data on an ASN, IXP, facility or org, the same form as the page's "Report incorrect data" link. subject_type: asn | ixp | facility | org. subject_id is the ASN number, PeeringDB id (ixp/facility), or org registry id/slug. |
| list_watch_rule_types | The alert-rule vocabulary: every subject/event combination a rule can be built on, the optional conditions each supports, and - just as importantly - the combinations that are deliberately WITHHELD because nothing in the system emits the event, each with the silent table and the reason. Call this before create_watch_rule. A rule type that is not listed as offered cannot be created, and asking for one returns the reason rather than an empty result. |
| create_watch_rule | Create an alert rule. rule_key comes from list_watch_rule_types; subject_value is the ASN, the PeeringDB exchange or facility id, or a registry key like "arin:C12345678"; predicate_key and predicate_value are optional and must be one of the conditions that rule type supports; frequency is daily | weekly | monthly. Evaluated by the nightly job, so the first result arrives with the next run - a new rule subscribes you to its subject's FUTURE and never replays history. Requires the Search Scout feature. |
| preview_watch_rule | Dry-run an alert rule before creating it: what would it have matched over the last N days? Same fields as create_watch_rule (rule_key, subject_value, predicate_key, predicate_value), no rule is saved. A refusal (evaluable:false) means the preview could not be run - it does NOT mean the rule matches nothing. |
| list_watch_rules | Your alert rules and your org-shared ones, each with the outcome of its last evaluation. A status of "unevaluable" means the rule could NOT be run - it is not zero matches. A status of "never_observed" means the rule's event types have never produced a row anywhere, which is different again. |
| delete_watch_rule | Delete one of your alert rules by id, with its evaluation history. Scoped to rules you own or your org shares; a rule you cannot see is reported as not found. |
| set_watch_rule_active | Enable or disable one of your alert rules by id, WITHOUT deleting its evaluation history the way delete_watch_rule + create_watch_rule would. Requires the Search Scout feature.. |
| update_watch_rule | Change an existing alert rule's settings by id: name, frequency, predicate_key/predicate_value, share_with_org, active. What the rule watches (its rule_key/subject) cannot be changed this way - delete_watch_rule + create_watch_rule for that. Fields you omit are left unchanged. Requires the Search Scout feature. |
| trigger_domain_scan | Queue a new Domain Intelligence scan for a domain. Requires Domain Intelligence access and an organization (subject to your org's daily/monthly scan quota). Runs asynchronously - poll get_domain_intel(domain) for the result; status starts "queued". |
| rerun_domain_scan | Re-scan a domain from a previous scan you can see (yours, your org's, or any if you are an admin), linked to it via previous_scan_id. Same quota and access rules as trigger_domain_scan. |
| delete_domain_scan | Delete a Domain Intelligence scan and its records. Must be a scan you can see (yours, your org's, or any if you are an admin) - same not-found error either way for a foreign or nonexistent id. This is permanent; there is no undo. |
| tag_prospect | Tag an ASN as a sales prospect. tag: not_interested | prospected | customer | high_priority. Dismissing a lead at /prospect (the "Not interested" action) is the same write as tag_prospect(tag: 'not_interested') - there is no separate dismiss tool. |
| untag_prospect | Remove your prospect tag from an ASN, whatever it is. Scoped to your own row only. To specifically undo a not_interested dismissal - including one an org-mate applied - use undismiss_prospect instead. |
| undismiss_prospect | Undo a "not interested" dismissal on an ASN - yours or any dismissal by your sales org - without touching a different tag (customer/prospected/high_priority) that might also be set. Matches POST /prospect/undismiss exactly; NOT the same as untag_prospect, which only removes your own tag and is not restricted to the not_interested tag. |
| save_search | Save a search by name and its URL (e.g. "/asns?country=CA&cdn=akamai") |
| delete_saved_search | Delete one of your saved searches by id (id from get_saved_searches).. |
| create_auto_scout | Create an auto-scout that re-runs a saved search on a schedule and alerts when the result count changes |
| set_auto_scout_frequency | Change an auto-scout run frequency (daily | weekly | monthly) by its id |
| delete_auto_scout | Delete one of your auto-scouts by id. Requires access level 100 and is scoped to your user_id. |
| create_client | Create a CRM client/account in your sales org. tag: customer | prospect | not_interested | partner | high_priority |
| add_asn_to_client | Add an ASN to one of your CRM clients (client_id from get_prospect_tags or create_client) |
| remove_asn_from_client | Remove an ASN from one of your CRM clients, without deleting the client.. |
| delete_client | Delete one of your CRM clients/accounts and all its members. Scoped to clients you own or your sales org shares.. |
| mark_alerts_read | Mark your alerts read - all of them, or one by alert_id |
| archive_alerts | Archive and mark read all alerts belonging to you and your sales org, matching /alerts/archive-all. |
| dismiss_notification | Dismiss one of your in-app notifications by id (from get_notifications), or all of them if id is omitted. |
| archive_trace_batch | Archive one of your submitted traceroute batches, matching the Archive button on /my/traceroutes. Archived batches are hidden from get_my_traceroutes unless archived:true is passed. |
| unarchive_trace_batch | Restore an archived traceroute batch of yours. |
| exclude_trace_batch_from_transit | Include or exclude one of your traceroute batches from the Transit Analyser, matching /my/traceroutes' per-batch toggle. exclude:true removes it from that wizard's candidate pool without deleting or archiving the batch. |
| update_seller_profile | Update your sales org seller profile (sells_transit/colocation/transport/ixp/consulting/cloud_connect booleans; transit_max_speed, transit_focus, transit_regions, target_market_size, notes) |
| create_alert_destination | Register a new webhook alert destination. Created DISABLED (enabled:false) - never armed by the act of saving it; enable it explicitly via update_alert_destination once you have confirmed it with test_alert_destination. url is SSRF-validated at save time (HTTPS only; loopback/RFC1918/link-local/CGNAT/multicast/reserved/cloud-metadata ranges rejected for IPv4 and IPv6; no embedded credentials; port 443 only) and re-validated fresh on every future delivery attempt, since a DNS answer can change after the row is saved. Returns a one-time HMAC signing secret (webhook_secret) for verifying the BGPScout-Signature header on what you receive - shown ONLY on this call; if lost, rotate it via update_alert_destination's rotate_secret. type:"email" is refused: this deployment has no mail provider configured, so an email destination could never receive anything - only "webhook" is accepted. |
| update_alert_destination | Change one of your alert destinations by id. Fields you omit are left unchanged. url (webhook only) is SSRF-validated again at update time, and if it actually changes, the destination is automatically set back to disabled - the same "not armed until confirmed" rule create_alert_destination applies, since the delivery target itself has changed. rotate_secret:true issues a brand-new HMAC signing secret (returned once, as webhook_secret) while the previous one stays valid for 24h so an in-flight receiver update is not broken mid-rotation; it does not touch label/url/enabled and cannot be combined with changing them in a way that skips the disable - pass it alone if that is all you want to do. A destination_id you do not own is reported exactly like one that does not exist. |
| delete_alert_destination | Permanently delete one of your alert destinations by id: its signing secret, its watch_rule opt-ins (watch_rule_delivery), and - cancelling rather than erasing, so delivery history stays auditable - any still-pending queued deliveries for it. Already-delivered/dead delivery history (alert_delivery_queue / alert_delivery_attempt rows) is retained. A destination_id you do not own is reported exactly like one that does not exist. |
| test_alert_destination | Send a clearly-labelled synthetic test event (test:true in the envelope) to one of your webhook destinations and report the outcome. Does NOT create a real watchlist_alert and never touches the delivery queue, so it cannot be mistaken for a real alert on either end. Only ever sends to the destination's own already-validated, already-stored URL - never a caller-supplied one - so this cannot be used to probe an arbitrary address, and SSRF validation still re-runs fresh on every call regardless. Rate-limited per caller (5 per rolling 5 minutes), since this is the one tool in this group that reaches the network. A destination_id you do not own is reported exactly like one that does not exist. |
| set_watch_rule_delivery | Opt one of your watch rules into delivery at one of your alert destinations, or withdraw it (enabled:false). This is what actually arms delivery: nothing is queued for a rule that has no enabled opt-in row, so a rule and a destination that both exist still deliver nothing until this is called. Ownership of BOTH ids is checked in SQL, and an id belonging to someone else fails exactly like one that does not exist, so it cannot be used to probe for other users' ids. Idempotent: calling it twice with the same values changes nothing. Withdrawing (enabled:false) keeps the row so the history of the opt-in survives; delete_alert_destination removes the rows outright. NOTE what is queued is WIDER than this rule's event types: dispatch matches on the rule's SUBJECT, so a rule watching AS X's IXP membership also routes AS X's registry alerts to this destination. That is deliberate (it can only add a message you can silence, never drop one you asked for), and that behaviour is deliberate. |
With 171 tools, you can narrow what an assistant loads (e.g. OpenWebUI's Function Name Filter List):
lookup_asn,lookup_ip,lookup_prefix,get_asn_bogons,get_asn_prefixes,get_asn_facilities,get_asn_ixps,search_asns,get_facility_members,find_near,search_allocations,get_asn_allocations,search_transfers,lookup_rir_orgfind_networks_near,find_transit_near,find_upstreams_near,find_networks_at_facilities_near,find_networks_on_ixps_near,find_near,lookup_ip,get_facility_members,get_ixp_membersfind_growing_networks,get_asn_growth,find_networks_at_facilities_near,find_networks_on_ixps_near,lookup_asn,get_eventslookup_asn,search_asns,find_prospects,find_ddos_protected,get_lead_reasons,get_buy_signals,get_top_networks,find_transport,tag_prospect,create_client,add_asn_to_client,search_dormant_ipv4lookup_asn,lookup_ip,get_asn_history,get_route_history,get_bgp_state,get_asn_neighbours,get_transit_providers,find_upstreams_near,search_traceroutes,get_route_heatmap,get_tier1_comparison,get_tier1_city_view,get_country_scoreboard,compare_asns,recommend_transit