{
  "name": "link-extract",
  "summary": "Fetches a web page and returns every link on it, resolved to absolute, each tagged with its rel, a nofollow flag and whether it stays on the same site.",
  "rationale": "It needs the network and a real DOM: resolving relative and protocol-relative hrefs against a possible <base>, classifying internal versus external, and reading rel/nofollow correctly is fiddly to get right, and feeding raw HTML to a model to do it costs far more in tokens than this call costs.",
  "family": "fetch",
  "price_usd": "0.002",
  "free": false,
  "timeout_ms": 8000,
  "max_timeout_seconds": 29,
  "input_schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "url": {
        "type": "string",
        "maxLength": 2048,
        "format": "uri",
        "description": "Absolute http(s) URL of the HTML or XHTML page to read, up to 2048 characters; up to 5 redirects are followed and links resolve against the final URL or its <base href>."
      },
      "internal_only": {
        "default": false,
        "description": "When true, return only links whose host equals the host of the fetched page (the final redirect hop); the totals then count only those links.",
        "type": "boolean"
      }
    },
    "required": [
      "url"
    ],
    "additionalProperties": false
  },
  "input_example": {
    "url": "https://grist.tools/samples/web/fieldnotes/index.html",
    "internal_only": false
  },
  "output_example": {
    "url": "https://grist.tools/samples/web/fieldnotes/index.html",
    "source_url": "https://grist.tools/samples/web/fieldnotes/index.html",
    "final_url": "https://grist.tools/samples/web/fieldnotes/index.html",
    "http_status": 200,
    "base_url": "https://grist.tools/samples/web/fieldnotes/index.html",
    "links": [
      {
        "href": "https://grist.tools/samples/web/fieldnotes/index.html",
        "text": "Home",
        "rel": null,
        "nofollow": false,
        "internal": true
      },
      {
        "href": "https://grist.tools/samples/web/fieldnotes/about.html",
        "text": "About",
        "rel": null,
        "nofollow": false,
        "internal": true
      },
      {
        "href": "https://grist.tools/samples/web/fieldnotes/feed.xml",
        "text": "Subscribe",
        "rel": "alternate",
        "nofollow": false,
        "internal": true
      },
      {
        "href": "https://grist.tools/samples/web/fieldnotes/notes/first-frost.html",
        "text": "The first frost of the season",
        "rel": null,
        "nofollow": false,
        "internal": true
      },
      {
        "href": "https://grist.tools/samples/web/fieldnotes/notes/rain-gauge.html",
        "text": "Reading a rain gauge by hand",
        "rel": null,
        "nofollow": false,
        "internal": true
      },
      {
        "href": "https://example.org/glossary",
        "text": "an outside glossary",
        "rel": "nofollow noopener",
        "nofollow": true,
        "internal": false
      },
      {
        "href": "https://grist.tools/samples/web/fieldnotes/sitemap-index.xml",
        "text": "Sitemap",
        "rel": null,
        "nofollow": false,
        "internal": true
      }
    ],
    "links_total": 7,
    "links_returned": 7,
    "links_truncated": false,
    "fetched_at": "2026-09-01T12:00:00.000Z"
  },
  "errors": [
    "invalid_input",
    "blocked_target",
    "unreachable_target",
    "upstream_status",
    "upstream_timeout",
    "unsupported_content_type",
    "too_large",
    "unprocessable",
    "internal"
  ]
}