List a website's URLs from its sitemaps, feeds, homepage links and a short crawl, each with its sources and last-modified date.

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

Every URL comes once, in one clean spelling, and says which sources listed it and when the site last changed it, where the site says so. URLs come in the order the site presents them: the homepage, its links, each sitemap in file order, the feeds, then pages found by the crawl. The same site gives the same order every time.

Send the parameters as a query string with GET, or as a JSON object (up to 1 MB) with POST to the same path. For many domains, start a job.

Parameters

Headers

NameTypeRequiredDefaultDescription
AuthorizationstringYes–Bearer followed by your API key. See Authentication.

Query parameters

NameTypeRequiredDefaultDescription
domainstringYes, or url–The website, such as example.com. www.example.com is read as given. Write http://example.com for a site without HTTPS. Send domain or url, not both.
urlstringInstead of domain–A full URL on the site; the whole site is mapped from its homepage.
limitintegerNo5000How many URLs to return, 1–50,000. Reading stops once there are this many.
crawlintegerNo10How many of the site's pages to visit for more links, besides the homepage, 0–100.
subdomainsbooleanNofalsetrue also keeps URLs on subdomains such as blog.example.com.
searchstringNo–Only URLs containing these words, such as pricing or blog 2026. Matching pages are crawled first.
sourcesstringNositemaps,feeds,linksWhere to look, comma-separated: sitemaps, feeds, links (homepage links and the crawl).
maxAgeintegerNo3600How old a stored answer may be, in seconds, up to 2,592,000. 0 maps fresh. An answer cut short by time or a busy lane is never reused.
timeoutintegerNo30How long the call may take, in seconds, 1–60.

Response

FieldTypeDescription
domain, startUrlstringThe site, and the homepage address we started from.
finalUrl, statusstring, integer | nullThe homepage after redirects, and its HTTP status.
completebooleantrue when every source was read to the end. false when a limit stopped us; everything found is still returned.
stoppedBystring[]What stopped us: limit, time, files (100 sitemap files), bytes (50 MB), fetches or busy.
countsobjecturls, returned, fromSitemaps, fromFeeds, fromLinks, withLastmod, duplicatesRemoved, outsideDomain, disallowedByRobots, invalid, notMatching, overCap, sitemapsNotRead, fetches, bytes.
sitemapsarrayEach sitemap file: url, from (robots.txt, homepage, guess, index), status, kind, urls, children, parent, lastmod, gzip, bytes, truncated, error.
feedsarrayEach feed: url, from, status, kind (rss, rdf, atom, jsonFeed), urls, bytes, truncated, error.
crawlobjectpagesFetched, maxPages, maxDepth, the site's crawlDelay, ok, failed, disallowed, the homepage and the robotsTxt read.
urlsarray{url, sources, lastmod}. sources: homepage, sitemap, feed, crawl. lastmod is the site's own date (UTC) or null.
truncatedbooleantrue when the site has more URLs than limit.
timingobjecttotalMs, and waitMs: time spent waiting for this site's pace and for our fetch capacity.
cached, ageSecondsboolean, integerWhether this answer came from our store, and how old it is.

One spelling per page

Scheme and host are lowercased; default ports, #fragments and tracking parameters (utm_*, gclid, fbclid, msclkid, Mailchimp, HubSpot, Matomo and similar) are removed; ./ and ../ are resolved. www.example.com and example.com count as one site, written the way its homepage writes itself, and http:// links on an HTTPS site become https://. When a crawled page names a rel=canonical, that URL is listed.

Large sites

We read up to 100 sitemap files, 5 feeds and 50 MB per call, follow sitemap indexes 3 levels deep, and unzip .gz sitemaps. A site with many sitemap files can take longer than one call: the answer then has complete: false and stoppedBy: ["time"]. Call again with a longer timeout: the call starts over.

How we fetch

We fetch as Rankfield-User (Mozilla/5.0 (compatible; Rankfield-User/1.0; +https://rankfield.dev/bot)), as plain HTML without running JavaScript.

  • robots.txt: obeyed on every request and redirect, following the rules for Rankfield-User, or for RankfieldBot when the file has none for it, or * when it has neither. Sitemaps and pages it disallows are counted in disallowedByRobots and never fetched.
  • Firewalls: a challenge, CAPTCHA or block page is reported in the answer, never worked around and never retried.
  • Never: cookies, logins, JavaScript, proxies or other bots' names.
  • Excluded sites: Reddit, LinkedIn, Facebook, Instagram, X/Twitter and TikTok (subdomains included), Google search result pages, and sites whose owners asked us are never fetched.
  • Pace: each website is fetched gently, shared by all customers, slower when its robots.txt sets a Crawl-delay or after a slow answer. Many URLs on one site go at that site's pace.

Repeat calls to the same website within about a minute can wait their turn for that site's slot. If the wait runs past the call's time limit (30 seconds by default), the call ends with 504 timeout and details.reason sitePace. It doesn't count as usage: retry after Retry-After, or allow a longer timeout.

More: Rankfield crawlers.

Errors

Errors are {"error": {"code", "message", "details"}}. Branch on code; message is for people.

StatusCodeWhen
400invalid_inputA parameter is missing, unknown or malformed.
401unauthorizedNo valid API key.
403robots_disallowedrobots.txt disallows the homepage and nothing else on the site may be read (for example Disallow: /, or a robots.txt that answers with a server error).
403site_excludedThe site is on the exclusion list.
403blockedThe website's bot protection refused us. Reported at once and never worked around; details says which protection. Not counted as usage.
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). Not counted as usage. Retry after Retry-After seconds.
500internalOur bug. Retry.
502fetch_failedThe website could not be reached at all (DNS, connection, TLS).
503unavailableThe service is restarting or briefly unavailable. Retry after Retry-After. (A full fetch capacity answers 429 busy.)
504timeoutThe website didn't answer within the call's time limit. When details.reason is 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.

A sitemap or page that fails is reported in its own entry, not as an error. Only answered calls and the 502 and website-side 504 errors count as usage; refusals, our own errors (500, 503), 429 busy and a 504 with details.reason sitePace don't.

Limits

No per-key limits. When the server is busy you get 429 busy with Retry-After. Waiting calls are served in turn across keys, so a key with a few calls is never stuck behind one with thousands.

MCP

On the MCP server, the tool for this is map_site.