Site Map API

Lists a website's URLs from what the site publishes and links: the sitemaps its robots.txt names, its RSS, Atom and JSON feeds, the links on its homepage and a short crawl of its pages. Every URL comes once, with the sources that listed it and its last-modified date where the site gives one.

Need the links of one page only? Use Link Extraction.

GEThttps://rankfield.dev/v1/map?domain={domain}

Send Authorization: Bearer YOUR_API_KEY. Get a free key; no account needed.

Parameters#

NameTypeDescription
domainquery, required unless urlstringThe website, e.g. example.com. Write http://example.com for a site without HTTPS. Send domain or url, not both.
urlquery, optionalstringInstead of domain: a full URL on the site. We map the whole site from its homepage.
limitquery, optionalintegerdefault 5000URLs to return, 1 to 50,000. We stop reading once we have this many.
crawlquery, optionalintegerdefault 10Pages to visit for more links besides the homepage, 0 to 100.
subdomainsquery, optionalbooleandefault falsetrue also keeps URLs on subdomains such as blog.example.com.
sourcesquery, optionalstringdefault all threeComma-separated: sitemaps, feeds, links.
maxAgequery, optionalintegerdefault 3600Seconds a stored answer may be old (default 1 hour), up to 2,592,000. 0 reads the site fresh.
timeoutquery, optionalintegerdefault 30Seconds the call may take, 1 to 60.

Response#

FieldTypeDescription
domain startUrl finalUrl statusstring, integerThe site, the homepage we started from, the one that answered and its HTTP status.
complete stoppedByboolean, arraycomplete: false when a limit stopped us: limit, time, files, bytes, fetches or busy. What we found still comes back.
countsobjectURLs found and returned, per source, with lastmod, duplicates removed, outside the domain, disallowed by robots.txt, and fetches and bytes.
sitemaps feedsarrayEach file read: url, where we found it, status, kind, how many urls, bytes, truncated, error.
crawlobjectPages fetched, the site's crawlDelay, failures, and the robots.txt we read.
urlsarray{url, sources, lastmod}. sources lists homepage, sitemap, feed, crawl; lastmod is the site's own date or null.
truncated timingboolean, objecttrue when the site has more URLs than limit; totalMs and waitMs.
cached ageSecondsboolean, integerWhether the answer came from our store, and its age.

Errors#

Errors come as {"error":{"code":"…","message":"…","details":{…}}} with the HTTP status.

StatusCodeWhen
400invalid_inputA parameter is missing, unknown or malformed.
401unauthorizedThe key is missing, unknown or revoked.
403robots_disallowedrobots.txt disallows the homepage and nothing else on the site may be read (e.g. Disallow: /, or a robots.txt that answers 5xx).
403blockedThe website's bot protection refused us. Reported at once and never worked around; details says which protection. Not counted as usage.
403site_excludedThe site is on the exclusion list: Reddit, LinkedIn, Facebook, Instagram, X/Twitter and TikTok (subdomains included), Google search result pages, and sites whose owners asked us.
422address_not_publicThe host resolves to a private or internal address.
429busyOur fetch capacity is busy: the call was refused at once, waited 4 seconds without a turn, or its time limit ran out while it waited (then details.reason is capacity). A new job also gets it when the shared job queue is full (Retry-After 60). Not counted as usage. Retry after Retry-After seconds.
500internalOur bug. Retry.
502fetch_failedThe website could not be reached (DNS, connection, TLS, more than 5 redirects or a bad one, a malformed answer); details.reason says which.
503unavailableThe service is restarting or briefly unavailable. Retry after Retry-After. (A full fetch capacity answers 429 busy.)
504timeoutThe website did not answer within timeout. With details.reason: "sitePace", the call waited its turn for this website instead, which we fetch gently, shared by all customers. Retry after Retry-After, or allow a longer timeout.

Price and limits#

  • Free; no per-call charge.
  • No per-key limits. When the server is busy you get 429 busy with Retry-After.
  • Only answered calls count as usage (plus the website errors listed below); our own errors (500, 503), 429 busy and a 504 with details.reason sitePace don't.

Up to 1,000 sites in one job#

POST /v1/map/jobs with an Idempotency-Key header and {"items":[{"domain":"example.com"}, …]}. Each item takes the same parameters as one call, except timeout: items use the default, 30 seconds. Every item is checked first: if any is invalid, the whole request is a 400 that lists the first 20. A new job answers 202; the same Idempotency-Key with a byte-identical body returns the same job (200, Idempotency-Replayed: true), and with any other body is a 409.

Read GET /v1/map/jobs/{id}/results?cursor=0&limit=100: finished items as they complete, each {index, ok, result} or {index, ok: false, error}. Pass cursor back until hasMore is false. POST /v1/map/jobs/{id}/cancel stops a job. A job runs 16 items at a time; an item that meets busy waits and retries by itself. Results are kept 24 hours.

Send as many jobs as you like. Jobs from all keys share one queue: when 20,000 items are already waiting, a new job answers 429 busy with Retry-After (60 seconds).

Reference: https://rankfield.dev/docs/get-started/batch-jobs

Notes#

  • URLs come in the order the site presents them, one spelling per page: tracking parameters and session ids removed, rel=canonical followed on crawled pages. We read up to 100 sitemap files, 5 feeds and 50 MB per call. An answer cut short by the time limit or a busy lane isn't reused: call again with a longer timeout and it starts over.
  • We fetch as Rankfield-User, obey robots.txt on every request and redirect, run no JavaScript, use no cookies or proxies, and never work around a firewall. We fetch each site gently, shared by all customers. More: rankfield.dev/bot.

See also#