Skip to content

harn fetch

harn fetch [options] <url>

An HTTP request that writes the response body to stdout and speaks to the shared cookie jar. By default it attaches any matching cookies from ~/.cache/harnery/cookies.json to the request and persists Set-Cookie responses back (the same jar harn browse and harn cookies use). harn fetch never opens the browser profile, so a sign-in reaches it through the jar: when a harn browse --login window closes, the sign-in site’s cookies (its registrable domain and subdomains, nothing else) are read out of the profile and merged into the jar, and later harn fetch calls to that site are signed in (details). Cookies harn fetch receives flow back to later harn browse runs the same way. Hosts that compose Harnery can also inject cookies through HarneryProgramContext.extraCookies (applied before the Cookie header is built; skipped with --no-cookies). Pipe it into harn read for the canonical scrape-to-markdown flow: harn fetch <url> | harn read -.

Flag Description
-X, --method <method> HTTP method. Default GET.
-H, --header <header...> Extra request header, format Name: value. Repeatable.
-d, --data <body> Request body.
-o, --output <file> Write the response body to a file instead of stdout.
--store <path> Cookie store path. Default ~/.cache/harnery/cookies.json.
--no-cookies Skip cookie-jar attach and persist (clean session). Also skips a host extraCookies hook.
--no-pace Skip the human-pace gap between requests to the same site for this run. See Human pace.
--redirect <mode> Redirect handling: follow | error | manual. Default follow.
--timeout <ms> Request timeout in milliseconds. Default 30000.
--status Print the status line to stderr.
--headers Print response headers to stderr.
--json Output the full FetchResult (status, headers, body) as JSON.

Requests are paced like every other Harnery page load: consecutive requests to the same site wait a random 3 to 9 seconds, shared across processes through ~/.cache/harnery/pace.json, with local and private hosts exempt. --no-pace skips it for one run and HARNERY_PACE=off disables it machine-wide. The full set of controls is in Human pace.

A host that composes Harnery can check every -H header through HarneryProgramContext.headerPolicy(host, name, value) before the request is sent. A refusal ends the run with the host’s message and a nonzero exit, and no flag overrides it. Only your -H headers are checked; the default User-Agent and headers the host adds through extraHeaders are not. harn on its own installs no policy.

With the default --redirect follow, Harnery follows redirects itself rather than leaving them to the runtime, with or without the cookie jar. Each hop gets the jar cookies for its own host, the host’s extraHeaders for its own URL, and its own Set-Cookie persistence.

A redirect within the same origin keeps every header you passed. A redirect to a different origin keeps only User-Agent, Accept, Accept-Language, and Range; every other -H header, including Authorization, Cookie, API keys, and custom X-* headers, is dropped for the rest of the chain. A 303, or a 301 or 302 on a request other than GET or HEAD, continues as a GET without the body. A 307 or 308 to a different origin on a request other than GET or HEAD stops with an error instead of resending the body; request the new URL directly if that is what you want.

The body goes to stdout by default. With --output, Harnery writes the HTTP representation bytes exactly as received, before content decoding, regardless of the file extension or Content-Type. Byte-mode requests ask the server for identity encoding and do not automatically decompress a response that still carries Content-Encoding, so --headers continues to describe the saved file. The Bun runtime supports this directly. The Node fallback fails instead of saving altered bytes if a server ignores the identity request and returns content-encoded data. Stdout and --json decode the body as UTF-8 text, so use --output for binary files and text in another encoding. --json emits the whole FetchResult ({ status, statusText, url, headers, body, cookiesSaved }) as a single JSON object. When cookies are saved back to the jar, a note is logged to stderr.

Terminal window
# GET a page (cookies auto-attached and persisted)
harn fetch https://example.com
# The canonical scrape-to-markdown pipeline
harn fetch https://example.com | harn read -
# POST JSON with explicit headers
harn fetch https://example.com/api -X POST -H 'Content-Type: application/json' -d '{"k":"v"}'
# Inspect a redirect response without following it
harn fetch https://example.com/login --redirect manual --headers
# Clean session, full result as JSON
harn fetch https://example.com --no-cookies --json
# Save a binary response to a file
harn fetch https://example.com/image.png -o /path/to/image.png