API Reference

Reference request parameters, paging behavior, response fields, and production-safe client patterns for documented API endpoints.

Search API

Endpoint Parameter Mode
/search/fqdn fqdn JSON Exact record lookup
/search/ip ip NDJSON/JSON FQDN page/stream by exact IP
/search/prefix prefix NDJSON/JSON FQDN page/stream by CIDR prefix
/search/asn asn NDJSON/JSON FQDN page/stream by ASN
/search/registered_domain registered_domain fqdn_prefix NDJSON/JSON FQDN page/stream by registered domain
/search/tld tld NDJSON/JSON FQDN page/stream by TLD

GET /search/fqdn

Exact FQDN record lookup.

Query parameters
  • fqdn required
  • limit optional, default 100, max 10000, further clamped by request policy
  • count_only optional boolean; returns metadata only with an empty results array
Response
[
  {
    "id": "747709867",
    "fqdn": "www.example.com",
    "registered_domain": "example.com",
    "tld": "com",
    "first_seen": "2026-01-29 20:09:06.440014+00",
    "obs_id": "2172612462",
    "observed_at": "2026-03-03 11:41:49.120617+00",
    "ip": "203.0.113.10",
    "prefix": "203.0.113.0/24",
    "asn": "13335",
    "as_name": "CLOUDFLARENET",
    "country": "US",
    "rir": "ARIN",
    "ttl": "300",
    "record_type": "A"
  }
]

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
params = {"fqdn": "www.example.com", "limit": 10}

r = requests.get(f"{API_BASE}/search/fqdn", params=params, headers=headers, timeout=30)
r.raise_for_status()
print(r.headers.get("X-Total-Count"), r.json())
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"

curl -sS -G "${API_BASE}/search/fqdn" \
  ${TOKEN:+-H "Authorization: Bearer ${TOKEN}"} \
  --data-urlencode "fqdn=www.example.com" \
  --data-urlencode "limit=10"
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

const u = new URL("/search/fqdn", API_BASE);
u.searchParams.set("fqdn", "www.example.com");
u.searchParams.set("limit", "10");

const r = await fetch(u, {
  headers: token ? { Authorization: `Bearer ${token}` } : {},
});
console.log(r.headers.get("X-Total-Count"), await r.json());

GET /search/ip

FQDN stream/page by exact IP.

Query parameters
  • ip required
  • limit, after_fqdn, contains, as_json, allow_empty, count_only
Response

JSON mode (as_json=true)

{
  "ip": "203.0.113.10",
  "total_count": 46670,
  "limit": 100,
  "after_fqdn": null,
  "next_after_fqdn": "v1:...",
  "has_more": true,
  "results": [
    "a.example.com",
    "b.example.com"
  ],
  "truncated": false
}

NDJSON mode (default)

{"fqdn": "a.example.com"}
{"fqdn": "b.example.com"}

NDJSON response headers

{
  "Content-Type": "application/x-ndjson",
  "X-Limit": "100",
  "X-Has-More": "true",
  "X-After-FQDN (optional)": "v1:...",
  "X-Next-After-FQDN (optional)": "v1:...",
  "X-Total-Count (optional)": "46670"
}

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
base_params = {
    "ip": "203.0.113.10",
    "limit": 100,
    "as_json": "true",
    "allow_empty": "true",
}
after_fqdn = None

while True:
    params = dict(base_params)
    if after_fqdn:
        params["after_fqdn"] = after_fqdn

    r = requests.get(f"{API_BASE}/search/ip", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()

    for fqdn in page.get("results", []):
        print(fqdn)

    if page.get("truncated"):
        print("Pagination stopped by access policy; authenticate or narrow the query.")
        break

    after_fqdn = page.get("next_after_fqdn")
    if not page.get("has_more") or not after_fqdn:
        break
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
AFTER_FQDN=""

while :; do
  curl_args=(
    -sS -G "${API_BASE}/search/ip"
    --data-urlencode "ip=203.0.113.10"
    --data-urlencode "limit=100"
    --data-urlencode "as_json=true"
    --data-urlencode "allow_empty=true"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi
  if [ -n "$AFTER_FQDN" ]; then
    curl_args+=(--data-urlencode "after_fqdn=${AFTER_FQDN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -r '.results[]?'
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.truncated')" = "true" ]; then
    printf '%s\n' "Pagination stopped by access policy; authenticate or narrow the query."
    break
  fi

  AFTER_FQDN=$(printf '%s\n' "$RESPONSE" | jq -r '.next_after_fqdn // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_more')" != "true" ] || [ -z "$AFTER_FQDN" ]; then
    break
  fi
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

const baseParams = {
  ip: "203.0.113.10",
  limit: "100",
  as_json: "true",
  allow_empty: "true",
};
let afterFqdn = "";

while (true) {
  const u = new URL("/search/ip", API_BASE);
  for (const [key, value] of Object.entries(baseParams)) {
    u.searchParams.set(key, String(value));
  }
  if (afterFqdn) {
    u.searchParams.set("after_fqdn", afterFqdn);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();

  for (const fqdn of page.results ?? []) {
    console.log(fqdn);
  }

  if (page.truncated) {
    console.log("Pagination stopped by access policy; authenticate or narrow the query.");
    break;
  }

  afterFqdn = page.next_after_fqdn ?? "";
  if (!page.has_more || !afterFqdn) break;
}

GET /search/prefix

FQDN stream/page by CIDR prefix.

Query parameters
  • prefix required
  • Supports limit, after_fqdn, contains, as_json, allow_empty, count_only
Response

JSON mode (as_json=true)

{
  "prefix": "203.0.113.0/24",
  "total_count": 302,
  "limit": 100,
  "after_fqdn": null,
  "next_after_fqdn": "v1:...",
  "has_more": true,
  "results": [
    "a.example.com",
    "b.example.com"
  ],
  "truncated": false
}

NDJSON mode (default)

{"fqdn": "a.example.com"}
{"fqdn": "b.example.com"}

NDJSON response headers

{
  "Content-Type": "application/x-ndjson",
  "X-Limit": "100",
  "X-Has-More": "true",
  "X-After-FQDN (optional)": "v1:...",
  "X-Next-After-FQDN (optional)": "v1:...",
  "X-Total-Count (optional)": "46670"
}

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
base_params = {
    "prefix": "203.0.113.0/24",
    "limit": 50,
    "as_json": "true",
    "allow_empty": "true",
}
after_fqdn = None

while True:
    params = dict(base_params)
    if after_fqdn:
        params["after_fqdn"] = after_fqdn

    r = requests.get(f"{API_BASE}/search/prefix", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()

    for fqdn in page.get("results", []):
        print(fqdn)

    if page.get("truncated"):
        print("Pagination stopped by access policy; authenticate or narrow the query.")
        break

    after_fqdn = page.get("next_after_fqdn")
    if not page.get("has_more") or not after_fqdn:
        break
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
AFTER_FQDN=""

while :; do
  curl_args=(
    -sS -G "${API_BASE}/search/prefix"
    --data-urlencode "prefix=203.0.113.0/24"
    --data-urlencode "limit=50"
    --data-urlencode "as_json=true"
    --data-urlencode "allow_empty=true"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi
  if [ -n "$AFTER_FQDN" ]; then
    curl_args+=(--data-urlencode "after_fqdn=${AFTER_FQDN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -r '.results[]?'
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.truncated')" = "true" ]; then
    printf '%s\n' "Pagination stopped by access policy; authenticate or narrow the query."
    break
  fi

  AFTER_FQDN=$(printf '%s\n' "$RESPONSE" | jq -r '.next_after_fqdn // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_more')" != "true" ] || [ -z "$AFTER_FQDN" ]; then
    break
  fi
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

const baseParams = {
  prefix: "203.0.113.0/24",
  limit: "50",
  as_json: "true",
  allow_empty: "true",
};
let afterFqdn = "";

while (true) {
  const u = new URL("/search/prefix", API_BASE);
  for (const [key, value] of Object.entries(baseParams)) {
    u.searchParams.set(key, String(value));
  }
  if (afterFqdn) {
    u.searchParams.set("after_fqdn", afterFqdn);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();

  for (const fqdn of page.results ?? []) {
    console.log(fqdn);
  }

  if (page.truncated) {
    console.log("Pagination stopped by access policy; authenticate or narrow the query.");
    break;
  }

  afterFqdn = page.next_after_fqdn ?? "";
  if (!page.has_more || !afterFqdn) break;
}

GET /search/asn

FQDN stream/page by ASN.

Query parameters
  • asn required
  • Supports limit, after_fqdn, contains, as_json, allow_empty, count_only
Response

JSON mode (as_json=true)

{
  "asn": "13335",
  "total_count": 7,
  "limit": 100,
  "after_fqdn": null,
  "next_after_fqdn": "v1:...",
  "has_more": true,
  "results": [
    "a.example.com",
    "b.example.com"
  ],
  "truncated": false
}

NDJSON mode (default)

{"fqdn": "a.example.com"}
{"fqdn": "b.example.com"}

NDJSON response headers

{
  "Content-Type": "application/x-ndjson",
  "X-Limit": "100",
  "X-Has-More": "true",
  "X-After-FQDN (optional)": "v1:...",
  "X-Next-After-FQDN (optional)": "v1:...",
  "X-Total-Count (optional)": "46670"
}

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
base_params = {
    "asn": "15169",
    "limit": 100,
    "as_json": "true",
    "allow_empty": "true",
}
after_fqdn = None

while True:
    params = dict(base_params)
    if after_fqdn:
        params["after_fqdn"] = after_fqdn

    r = requests.get(f"{API_BASE}/search/asn", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()

    for fqdn in page.get("results", []):
        print(fqdn)

    if page.get("truncated"):
        print("Pagination stopped by access policy; authenticate or narrow the query.")
        break

    after_fqdn = page.get("next_after_fqdn")
    if not page.get("has_more") or not after_fqdn:
        break
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
AFTER_FQDN=""

while :; do
  curl_args=(
    -sS -G "${API_BASE}/search/asn"
    --data-urlencode "asn=15169"
    --data-urlencode "limit=100"
    --data-urlencode "as_json=true"
    --data-urlencode "allow_empty=true"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi
  if [ -n "$AFTER_FQDN" ]; then
    curl_args+=(--data-urlencode "after_fqdn=${AFTER_FQDN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -r '.results[]?'
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.truncated')" = "true" ]; then
    printf '%s\n' "Pagination stopped by access policy; authenticate or narrow the query."
    break
  fi

  AFTER_FQDN=$(printf '%s\n' "$RESPONSE" | jq -r '.next_after_fqdn // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_more')" != "true" ] || [ -z "$AFTER_FQDN" ]; then
    break
  fi
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

const baseParams = {
  asn: "15169",
  limit: "100",
  as_json: "true",
  allow_empty: "true",
};
let afterFqdn = "";

while (true) {
  const u = new URL("/search/asn", API_BASE);
  for (const [key, value] of Object.entries(baseParams)) {
    u.searchParams.set(key, String(value));
  }
  if (afterFqdn) {
    u.searchParams.set("after_fqdn", afterFqdn);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();

  for (const fqdn of page.results ?? []) {
    console.log(fqdn);
  }

  if (page.truncated) {
    console.log("Pagination stopped by access policy; authenticate or narrow the query.");
    break;
  }

  afterFqdn = page.next_after_fqdn ?? "";
  if (!page.has_more || !afterFqdn) break;
}

GET /search/registered_domain

FQDN stream/page under a registered domain.

Query parameters
  • registered_domain required
  • fqdn_prefix optional prefix branch constraint
  • Supports limit, after_fqdn, contains, as_json, allow_empty, count_only
Response

JSON mode (as_json=true)

{
  "registered_domain": "example.com",
  "total_count": null,
  "limit": 100,
  "after_fqdn": null,
  "next_after_fqdn": "v1:...",
  "has_more": true,
  "results": [
    "app01.example.com",
    "app02.example.com"
  ],
  "truncated": false,
  "fqdn_prefix_input": "app",
  "fqdn_filter_suffix": "app.example.com",
  "total_count_reason": "filtered_total_not_precomputed"
}

NDJSON mode (default)

{"fqdn": "app01.example.com"}
{"fqdn": "app02.example.com"}

NDJSON response headers

{
  "Content-Type": "application/x-ndjson",
  "X-Limit": "100",
  "X-Has-More": "true",
  "X-After-FQDN (optional)": "v1:...",
  "X-Next-After-FQDN (optional)": "v1:...",
  "X-Total-Count (optional)": "5014",
  "X-FQDN-Prefix-Input (optional)": "app",
  "X-FQDN-Filter-Suffix (optional)": "app.example.com"
}
Notes
When fqdn_prefix is supplied, JSON mode includes fqdn_prefix_input and fqdn_filter_suffix in the payload. NDJSON mode can emit the same values as X-FQDN-Prefix-Input and X-FQDN-Filter-Suffix headers.

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
base_params = {
    "registered_domain": "example.com",
    "fqdn_prefix": "app",
    "limit": 100,
    "as_json": "true",
    "allow_empty": "true",
}
after_fqdn = None

while True:
    params = dict(base_params)
    if after_fqdn:
        params["after_fqdn"] = after_fqdn

    r = requests.get(f"{API_BASE}/search/registered_domain", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()
    print(page.get("fqdn_filter_suffix"))

    for fqdn in page.get("results", []):
        print(fqdn)

    if page.get("truncated"):
        print("Pagination stopped by access policy; authenticate or narrow the query.")
        break

    after_fqdn = page.get("next_after_fqdn")
    if not page.get("has_more") or not after_fqdn:
        break
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
AFTER_FQDN=""

while :; do
  curl_args=(
    -sS -G "${API_BASE}/search/registered_domain"
    --data-urlencode "registered_domain=example.com"
    --data-urlencode "fqdn_prefix=app"
    --data-urlencode "limit=100"
    --data-urlencode "as_json=true"
    --data-urlencode "allow_empty=true"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi
  if [ -n "$AFTER_FQDN" ]; then
    curl_args+=(--data-urlencode "after_fqdn=${AFTER_FQDN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -r '.results[]?'
  printf '%s\n' "$RESPONSE" | jq -r '.fqdn_filter_suffix // empty'

  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.truncated')" = "true" ]; then
    printf '%s\n' "Pagination stopped by access policy; authenticate or narrow the query."
    break
  fi

  AFTER_FQDN=$(printf '%s\n' "$RESPONSE" | jq -r '.next_after_fqdn // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_more')" != "true" ] || [ -z "$AFTER_FQDN" ]; then
    break
  fi
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

const baseParams = {
  registered_domain: "example.com",
  fqdn_prefix: "app",
  limit: "100",
  as_json: "true",
  allow_empty: "true",
};
let afterFqdn = "";

while (true) {
  const u = new URL("/search/registered_domain", API_BASE);
  for (const [key, value] of Object.entries(baseParams)) {
    u.searchParams.set(key, String(value));
  }
  if (afterFqdn) {
    u.searchParams.set("after_fqdn", afterFqdn);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();
  console.log(page.fqdn_filter_suffix ?? "");

  for (const fqdn of page.results ?? []) {
    console.log(fqdn);
  }

  if (page.truncated) {
    console.log("Pagination stopped by access policy; authenticate or narrow the query.");
    break;
  }

  afterFqdn = page.next_after_fqdn ?? "";
  if (!page.has_more || !afterFqdn) break;
}

GET /search/tld

FQDN stream/page for TLD/eTLD values.

Query parameters
  • tld required; accepts com, .com, co.example (normalized by API)
  • Supports limit, after_fqdn, contains, as_json, allow_empty, count_only
Response

JSON mode (as_json=true)

{
  "tld": "com",
  "total_count": 815,
  "limit": 100,
  "after_fqdn": null,
  "next_after_fqdn": "v1:...",
  "has_more": true,
  "results": [
    "a.example.com",
    "b.example.com"
  ],
  "truncated": false
}

NDJSON mode (default)

{"fqdn": "a.example.com"}
{"fqdn": "b.example.com"}

NDJSON response headers

{
  "Content-Type": "application/x-ndjson",
  "X-Limit": "100",
  "X-Has-More": "true",
  "X-After-FQDN (optional)": "v1:...",
  "X-Next-After-FQDN (optional)": "v1:...",
  "X-Total-Count (optional)": "46670"
}

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
base_params = {
    "tld": ".com",
    "limit": 100,
    "as_json": "true",
    "allow_empty": "true",
}
after_fqdn = None

while True:
    params = dict(base_params)
    if after_fqdn:
        params["after_fqdn"] = after_fqdn

    r = requests.get(f"{API_BASE}/search/tld", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()

    for fqdn in page.get("results", []):
        print(fqdn)

    if page.get("truncated"):
        print("Pagination stopped by access policy; authenticate or narrow the query.")
        break

    after_fqdn = page.get("next_after_fqdn")
    if not page.get("has_more") or not after_fqdn:
        break
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
AFTER_FQDN=""

while :; do
  curl_args=(
    -sS -G "${API_BASE}/search/tld"
    --data-urlencode "tld=com"
    --data-urlencode "limit=100"
    --data-urlencode "as_json=true"
    --data-urlencode "allow_empty=true"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi
  if [ -n "$AFTER_FQDN" ]; then
    curl_args+=(--data-urlencode "after_fqdn=${AFTER_FQDN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -r '.results[]?'
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.truncated')" = "true" ]; then
    printf '%s\n' "Pagination stopped by access policy; authenticate or narrow the query."
    break
  fi

  AFTER_FQDN=$(printf '%s\n' "$RESPONSE" | jq -r '.next_after_fqdn // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_more')" != "true" ] || [ -z "$AFTER_FQDN" ]; then
    break
  fi
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

const baseParams = {
  tld: ".com",
  limit: "100",
  as_json: "true",
  allow_empty: "true",
};
let afterFqdn = "";

while (true) {
  const u = new URL("/search/tld", API_BASE);
  for (const [key, value] of Object.entries(baseParams)) {
    u.searchParams.set(key, String(value));
  }
  if (afterFqdn) {
    u.searchParams.set("after_fqdn", afterFqdn);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();

  for (const fqdn of page.results ?? []) {
    console.log(fqdn);
  }

  if (page.truncated) {
    console.log("Pagination stopped by access policy; authenticate or narrow the query.");
    break;
  }

  afterFqdn = page.next_after_fqdn ?? "";
  if (!page.has_more || !afterFqdn) break;
}

ASN API

EndpointParameterPurpose
/asnasnSingle ASN metadata lookup.
/asns/allnoneASN directory listing (optional contains).
/asn/subnetsasnIPv4/IPv6 subnet lists (optional contains).
/asn/searchhandleHandle substring search.
/asn/by_ipipFind ASNs whose ranges contain an IP.

GET /asn

ASN details record.

Query parameters
  • asn required
Response
{
  "results": [
    {
      "asn": "13335",
      "handle": "CLOUDFLARENET",
      "description": "Cloudflare, Inc.",
      "rir": "ARIN"
    }
  ]
}

Code sample

print(requests.get(f"{API_BASE}/asn", params={"asn": "15169"}, timeout=30).json())
curl -sS -G "${API_BASE}/asn" --data-urlencode "asn=15169"
const u = new URL("/asn", API_BASE);
u.searchParams.set("asn", "15169");
console.log(await (await fetch(u)).json());

GET /asns/all

Offset-paginated ASN directory.

Query parameters
  • limit default 100, max 10000
  • offset default 0
  • contains optional substring on ASN or handle
Response
{
  "total_count": 85767,
  "limit": 100,
  "offset": 0,
  "has_next": true,
  "next_offset": 100,
  "results": [
    {
      "asn": "13335",
      "handle": "CLOUDFLARENET",
      "description": "Cloudflare, Inc.",
      "rir": "ARIN"
    }
  ]
}

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
params = {
    "limit": 50,
    "contains": "cloud",
    "offset": 0,
}

while True:
    r = requests.get(f"{API_BASE}/asns/all", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()

    for item in page.get("results", []):
        print(item)

    next_offset = page.get("next_offset")
    if not page.get("has_next") or next_offset is None:
        break
    params["offset"] = next_offset
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
OFFSET=0

while :; do
  curl_args=(
    -sS -G "${API_BASE}/asns/all"
    --data-urlencode "limit=50"
    --data-urlencode "contains=cloud"
    --data-urlencode "offset=${OFFSET}"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -r '.results[]?'

  NEXT_OFFSET=$(printf '%s\n' "$RESPONSE" | jq -r '.next_offset // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_next')" != "true" ] || [ -z "$NEXT_OFFSET" ]; then
    break
  fi
  OFFSET="$NEXT_OFFSET"
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

let offset = 0;
while (true) {
  const u = new URL("/asns/all", API_BASE);
  const params = {
    limit: "50",
    contains: "cloud",
    offset: String(offset),
  };
  for (const [key, value] of Object.entries(params)) {
    u.searchParams.set(key, value);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();

  for (const item of page.results ?? []) {
    console.log(item);
  }

  if (!page.has_next || page.next_offset === null) break;
  offset = page.next_offset;
}

GET /asn/subnets

ASN subnet inventory by IP family.

Query parameters
  • asn required
  • limit, offset, contains
Response
{
  "asn": "13335",
  "total_count": 5,
  "ipv4_count": 5,
  "ipv6_count": 0,
  "limit": 100,
  "offset": 0,
  "has_next": false,
  "next_offset": null,
  "results": [
    {
      "ip_version": "ipv4",
      "prefix": "203.0.113.0/24"
    }
  ]
}

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
params = {
    "asn": "15169",
    "limit": 25,
    "contains": "203.0.113.",
    "offset": 0,
}

while True:
    r = requests.get(f"{API_BASE}/asn/subnets", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()

    for item in page.get("results", []):
        print(item)

    next_offset = page.get("next_offset")
    if not page.get("has_next") or next_offset is None:
        break
    params["offset"] = next_offset
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
OFFSET=0

while :; do
  curl_args=(
    -sS -G "${API_BASE}/asn/subnets"
    --data-urlencode "asn=15169"
    --data-urlencode "limit=25"
    --data-urlencode "contains=203.0.113."
    --data-urlencode "offset=${OFFSET}"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -c '.results[]?'

  NEXT_OFFSET=$(printf '%s\n' "$RESPONSE" | jq -r '.next_offset // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_next')" != "true" ] || [ -z "$NEXT_OFFSET" ]; then
    break
  fi
  OFFSET="$NEXT_OFFSET"
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

let offset = 0;
while (true) {
  const u = new URL("/asn/subnets", API_BASE);
  const params = {
    asn: "15169",
    limit: "25",
    contains: "203.0.113.",
    offset: String(offset),
  };
  for (const [key, value] of Object.entries(params)) {
    u.searchParams.set(key, value);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();

  for (const item of page.results ?? []) {
    console.log(item);
  }

  if (!page.has_next || page.next_offset === null) break;
  offset = page.next_offset;
}

GET /asn/by_ip

Find ASNs whose ranges include an IP.

Query parameters
  • ip required
  • limit, offset
Response
{
  "ip": "1.0.0.1",
  "ip_version": "ipv4",
  "total_count": 2,
  "limit": 100,
  "offset": 0,
  "has_next": false,
  "next_offset": null,
  "results": [
    {
      "asn": "13335",
      "handle": "CLOUDFLARENET",
      "description": "Cloudflare, Inc.",
      "rir": "ARIN",
      "prefix": "1.0.0.0/24"
    }
  ]
}

Code sample

import requests

API_BASE = "https://YOUR_API_BASE"
TOKEN = "YOUR_TOKEN_OR_EMPTY"

headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
params = {
    "ip": "203.0.113.10",
    "limit": 20,
    "offset": 0,
}

while True:
    r = requests.get(f"{API_BASE}/asn/by_ip", params=params, headers=headers, timeout=30)
    r.raise_for_status()
    page = r.json()

    for item in page.get("results", []):
        print(item)

    next_offset = page.get("next_offset")
    if not page.get("has_next") or next_offset is None:
        break
    params["offset"] = next_offset
API_BASE="https://YOUR_API_BASE"
TOKEN="YOUR_TOKEN_OR_EMPTY"
OFFSET=0

while :; do
  curl_args=(
    -sS -G "${API_BASE}/asn/by_ip"
    --data-urlencode "ip=203.0.113.10"
    --data-urlencode "limit=20"
    --data-urlencode "offset=${OFFSET}"
  )
  if [ -n "$TOKEN" ]; then
    curl_args+=(-H "Authorization: Bearer ${TOKEN}")
  fi

  RESPONSE=$(curl "${curl_args[@]}")
  printf '%s\n' "$RESPONSE" | jq -r '.results[]?'

  NEXT_OFFSET=$(printf '%s\n' "$RESPONSE" | jq -r '.next_offset // empty')
  if [ "$(printf '%s\n' "$RESPONSE" | jq -r '.has_next')" != "true" ] || [ -z "$NEXT_OFFSET" ]; then
    break
  fi
  OFFSET="$NEXT_OFFSET"
done
const API_BASE = "https://YOUR_API_BASE";
const token = "YOUR_TOKEN_OR_EMPTY";

let offset = 0;
while (true) {
  const u = new URL("/asn/by_ip", API_BASE);
  const params = {
    ip: "203.0.113.10",
    limit: "20",
    offset: String(offset),
  };
  for (const [key, value] of Object.entries(params)) {
    u.searchParams.set(key, value);
  }

  const r = await fetch(u, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const page = await r.json();

  for (const item of page.results ?? []) {
    console.log(item);
  }

  if (!page.has_next || page.next_offset === null) break;
  offset = page.next_offset;
}

Pagination & Cursors

Flow matrix

Flow Send Read Stop
Search stream keyset
after_fqdn
has_more next_after_fqdn
has_more=false or no next cursor/header returned.
ASN offset
limit offset
has_next next_offset
has_next=false or next_offset=null.

GET /search/fqdn is an exact lookup and does not use a continuation cursor.

Errors & Limits

Status Codes

Code When Client action
400 Endpoint-specific malformed input, such as an invalid IP for /asn/by_ip. Fix the request value; most query validation failures are 422.
401 Missing/invalid bearer token on required-auth routes. Refresh auth and retry.
403 Feature or plan access denied. Use an allowed flow or update entitlement.
404 No matching records or resource, and an empty response was not requested/supported. Handle as not found; use allow_empty=true on supported Search stream endpoints when an empty page is acceptable.
422 Query validation or large contains-search constraint. Fix field/range errors or narrow large contains searches and retry.
429 Daily auth/token quota exceeded. Pause authenticated data requests until quota resets, then retry.
500 Backend or mirrored dataset access error. Retry later; report persistent failures with request context.
503 Authentication or backing service temporarily unavailable. Retry after a short delay.

Limit and signals

Limit/signal Meaning Client action
Effective caps Endpoint hard caps, commonly up to 10000, combine with plan/auth policy; public anonymous limits can be lower. Honor returned limits and do not assume the requested limit was fully applied.
Headers vs payload Search stream NDJSON exposes continuation metadata in headers; Search JSON mode exposes equivalent fields in payload. Parse Search metadata by response mode; ASN offset metadata is always in the JSON payload.
total_count: null Filtered Search can omit total and include a reason field. Treat total as unknown and continue only while Search cursor metadata permits.
Truncation truncated indicates the server stopped pagination before data exhaustion, such as anonymous first-page access policy. Authenticate or narrow scope before continuing; truncated Search responses do not provide a next cursor.