Scrape to Markdown
Fetch a URL and return clean Markdown of its main content.
curl --request POST \
--url https://api.hydrafetch.com/v1/web/markdown \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"url": "https://example.com",
"maxAge": 302400000,
"cacheOnly": true,
"storeInCache": true,
"preferStructure": true,
"async": true,
"onlyMainContent": true,
"includeTags": [
"article",
"main"
],
"excludeTags": [
".ad",
"#comments"
],
"removeBase64Images": true,
"blockAds": true,
"includeLinks": true,
"renderJs": true,
"waitFor": 15000,
"timeout": 60500,
"location": {
"country": "us",
"languages": [
"en-US",
"en"
]
},
"headers": {}
}
'import requests
url = "https://api.hydrafetch.com/v1/web/markdown"
payload = {
"url": "https://example.com",
"maxAge": 302400000,
"cacheOnly": True,
"storeInCache": True,
"preferStructure": True,
"async": True,
"onlyMainContent": True,
"includeTags": ["article", "main"],
"excludeTags": [".ad", "#comments"],
"removeBase64Images": True,
"blockAds": True,
"includeLinks": True,
"renderJs": True,
"waitFor": 15000,
"timeout": 60500,
"location": {
"country": "us",
"languages": ["en-US", "en"]
},
"headers": {}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://example.com',
maxAge: 302400000,
cacheOnly: true,
storeInCache: true,
preferStructure: true,
async: true,
onlyMainContent: true,
includeTags: ['article', 'main'],
excludeTags: ['.ad', '#comments'],
removeBase64Images: true,
blockAds: true,
includeLinks: true,
renderJs: true,
waitFor: 15000,
timeout: 60500,
location: {country: 'us', languages: ['en-US', 'en']},
headers: {}
})
};
fetch('https://api.hydrafetch.com/v1/web/markdown', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hydrafetch.com/v1/web/markdown",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://example.com',
'maxAge' => 302400000,
'cacheOnly' => true,
'storeInCache' => true,
'preferStructure' => true,
'async' => true,
'onlyMainContent' => true,
'includeTags' => [
'article',
'main'
],
'excludeTags' => [
'.ad',
'#comments'
],
'removeBase64Images' => true,
'blockAds' => true,
'includeLinks' => true,
'renderJs' => true,
'waitFor' => 15000,
'timeout' => 60500,
'location' => [
'country' => 'us',
'languages' => [
'en-US',
'en'
]
],
'headers' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hydrafetch.com/v1/web/markdown"
payload := strings.NewReader("{\n \"url\": \"https://example.com\",\n \"maxAge\": 302400000,\n \"cacheOnly\": true,\n \"storeInCache\": true,\n \"preferStructure\": true,\n \"async\": true,\n \"onlyMainContent\": true,\n \"includeTags\": [\n \"article\",\n \"main\"\n ],\n \"excludeTags\": [\n \".ad\",\n \"#comments\"\n ],\n \"removeBase64Images\": true,\n \"blockAds\": true,\n \"includeLinks\": true,\n \"renderJs\": true,\n \"waitFor\": 15000,\n \"timeout\": 60500,\n \"location\": {\n \"country\": \"us\",\n \"languages\": [\n \"en-US\",\n \"en\"\n ]\n },\n \"headers\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hydrafetch.com/v1/web/markdown")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"https://example.com\",\n \"maxAge\": 302400000,\n \"cacheOnly\": true,\n \"storeInCache\": true,\n \"preferStructure\": true,\n \"async\": true,\n \"onlyMainContent\": true,\n \"includeTags\": [\n \"article\",\n \"main\"\n ],\n \"excludeTags\": [\n \".ad\",\n \"#comments\"\n ],\n \"removeBase64Images\": true,\n \"blockAds\": true,\n \"includeLinks\": true,\n \"renderJs\": true,\n \"waitFor\": 15000,\n \"timeout\": 60500,\n \"location\": {\n \"country\": \"us\",\n \"languages\": [\n \"en-US\",\n \"en\"\n ]\n },\n \"headers\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hydrafetch.com/v1/web/markdown")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"https://example.com\",\n \"maxAge\": 302400000,\n \"cacheOnly\": true,\n \"storeInCache\": true,\n \"preferStructure\": true,\n \"async\": true,\n \"onlyMainContent\": true,\n \"includeTags\": [\n \"article\",\n \"main\"\n ],\n \"excludeTags\": [\n \".ad\",\n \"#comments\"\n ],\n \"removeBase64Images\": true,\n \"blockAds\": true,\n \"includeLinks\": true,\n \"renderJs\": true,\n \"waitFor\": 15000,\n \"timeout\": 60500,\n \"location\": {\n \"country\": \"us\",\n \"languages\": [\n \"en-US\",\n \"en\"\n ]\n },\n \"headers\": {}\n}"
response = http.request(request)
puts response.read_body{
"data": {
"url": "https://example.com",
"finalUrl": "https://example.com/",
"redirected": false,
"status": 200,
"cached": false,
"metadata": {
"title": "Example Domain",
"pageType": "article",
"wordCount": 214,
"description": "A short summary of the page, as published by the page itself.",
"language": "en",
"author": "Jane Doe",
"siteName": "Example Blog",
"publishedTime": "2026-01-05",
"image": "https://example.com/cover.png"
},
"warning": "<string>",
"usage": {
"creditsUsed": 1,
"creditsRemaining": 4999,
"freshness": "fresh"
},
"quality": {
"confidence": 0.94,
"complete": true,
"blocked": false
},
"markdown": "<string>",
"html": "<string>",
"rawHtml": "<string>",
"links": {
"internal": [
"<string>"
],
"external": [
"<string>"
]
},
"structured": {
"entities": [
{
"type": "Product",
"properties": {}
}
],
"jsonLd": [
{}
],
"microdata": [
{}
],
"opengraph": [
{}
],
"rdfa": [
{}
],
"appState": [
"<string>"
]
},
"summary": "<string>",
"json": {}
}
}{
"jobId": "019f3c09-6fae-740f-9257-10c2b6af7f43",
"status": "queued"
}Authorizations
Body
The URL to scrape. Must be http(s).
"https://example.com"
Serve from cache if a capture of this URL is younger than this many milliseconds. Omit for the default 24h window; 0 always fetches fresh. Capped at 7 days.
0 <= x <= 604800000Only serve from cache. If there is no fresh cached copy, return 404 instead of fetching.
Persist the capture for later re-extraction. Default true.
Preserve document structure (headings, lists, tables) over prose density — good for marketing and service pages. Default off.
Return a job id immediately instead of waiting for the result. Poll GET /v1/web/scrape/{id}.
Return only the main content, dropping nav/boilerplate. Default true.
CSS selectors to keep. When set, only matching elements are considered.
50["article", "main"]
CSS selectors to strip before extraction.
50[".ad", "#comments"]
Strip inline base64 images from the output. Default true.
Remove common ad and tracking elements. Default true.
Keep inline links in the markdown. Default false — dropping them is what keeps our output dense. Turn on for reference, API-docs and code pages where cross-references are content.
Force full page rendering for JavaScript-heavy pages. Left to the pipeline when omitted.
Extra milliseconds to let the page settle before capture.
0 <= x <= 30000Overall time budget for the request, in milliseconds.
1000 <= x <= 120000Show child attributes
Show child attributes
Extra request headers to send when fetching the page.
Show child attributes
Show child attributes
Response
Show child attributes
Show child attributes
curl --request POST \
--url https://api.hydrafetch.com/v1/web/markdown \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"url": "https://example.com",
"maxAge": 302400000,
"cacheOnly": true,
"storeInCache": true,
"preferStructure": true,
"async": true,
"onlyMainContent": true,
"includeTags": [
"article",
"main"
],
"excludeTags": [
".ad",
"#comments"
],
"removeBase64Images": true,
"blockAds": true,
"includeLinks": true,
"renderJs": true,
"waitFor": 15000,
"timeout": 60500,
"location": {
"country": "us",
"languages": [
"en-US",
"en"
]
},
"headers": {}
}
'import requests
url = "https://api.hydrafetch.com/v1/web/markdown"
payload = {
"url": "https://example.com",
"maxAge": 302400000,
"cacheOnly": True,
"storeInCache": True,
"preferStructure": True,
"async": True,
"onlyMainContent": True,
"includeTags": ["article", "main"],
"excludeTags": [".ad", "#comments"],
"removeBase64Images": True,
"blockAds": True,
"includeLinks": True,
"renderJs": True,
"waitFor": 15000,
"timeout": 60500,
"location": {
"country": "us",
"languages": ["en-US", "en"]
},
"headers": {}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://example.com',
maxAge: 302400000,
cacheOnly: true,
storeInCache: true,
preferStructure: true,
async: true,
onlyMainContent: true,
includeTags: ['article', 'main'],
excludeTags: ['.ad', '#comments'],
removeBase64Images: true,
blockAds: true,
includeLinks: true,
renderJs: true,
waitFor: 15000,
timeout: 60500,
location: {country: 'us', languages: ['en-US', 'en']},
headers: {}
})
};
fetch('https://api.hydrafetch.com/v1/web/markdown', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hydrafetch.com/v1/web/markdown",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://example.com',
'maxAge' => 302400000,
'cacheOnly' => true,
'storeInCache' => true,
'preferStructure' => true,
'async' => true,
'onlyMainContent' => true,
'includeTags' => [
'article',
'main'
],
'excludeTags' => [
'.ad',
'#comments'
],
'removeBase64Images' => true,
'blockAds' => true,
'includeLinks' => true,
'renderJs' => true,
'waitFor' => 15000,
'timeout' => 60500,
'location' => [
'country' => 'us',
'languages' => [
'en-US',
'en'
]
],
'headers' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hydrafetch.com/v1/web/markdown"
payload := strings.NewReader("{\n \"url\": \"https://example.com\",\n \"maxAge\": 302400000,\n \"cacheOnly\": true,\n \"storeInCache\": true,\n \"preferStructure\": true,\n \"async\": true,\n \"onlyMainContent\": true,\n \"includeTags\": [\n \"article\",\n \"main\"\n ],\n \"excludeTags\": [\n \".ad\",\n \"#comments\"\n ],\n \"removeBase64Images\": true,\n \"blockAds\": true,\n \"includeLinks\": true,\n \"renderJs\": true,\n \"waitFor\": 15000,\n \"timeout\": 60500,\n \"location\": {\n \"country\": \"us\",\n \"languages\": [\n \"en-US\",\n \"en\"\n ]\n },\n \"headers\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hydrafetch.com/v1/web/markdown")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"https://example.com\",\n \"maxAge\": 302400000,\n \"cacheOnly\": true,\n \"storeInCache\": true,\n \"preferStructure\": true,\n \"async\": true,\n \"onlyMainContent\": true,\n \"includeTags\": [\n \"article\",\n \"main\"\n ],\n \"excludeTags\": [\n \".ad\",\n \"#comments\"\n ],\n \"removeBase64Images\": true,\n \"blockAds\": true,\n \"includeLinks\": true,\n \"renderJs\": true,\n \"waitFor\": 15000,\n \"timeout\": 60500,\n \"location\": {\n \"country\": \"us\",\n \"languages\": [\n \"en-US\",\n \"en\"\n ]\n },\n \"headers\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hydrafetch.com/v1/web/markdown")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"https://example.com\",\n \"maxAge\": 302400000,\n \"cacheOnly\": true,\n \"storeInCache\": true,\n \"preferStructure\": true,\n \"async\": true,\n \"onlyMainContent\": true,\n \"includeTags\": [\n \"article\",\n \"main\"\n ],\n \"excludeTags\": [\n \".ad\",\n \"#comments\"\n ],\n \"removeBase64Images\": true,\n \"blockAds\": true,\n \"includeLinks\": true,\n \"renderJs\": true,\n \"waitFor\": 15000,\n \"timeout\": 60500,\n \"location\": {\n \"country\": \"us\",\n \"languages\": [\n \"en-US\",\n \"en\"\n ]\n },\n \"headers\": {}\n}"
response = http.request(request)
puts response.read_body{
"data": {
"url": "https://example.com",
"finalUrl": "https://example.com/",
"redirected": false,
"status": 200,
"cached": false,
"metadata": {
"title": "Example Domain",
"pageType": "article",
"wordCount": 214,
"description": "A short summary of the page, as published by the page itself.",
"language": "en",
"author": "Jane Doe",
"siteName": "Example Blog",
"publishedTime": "2026-01-05",
"image": "https://example.com/cover.png"
},
"warning": "<string>",
"usage": {
"creditsUsed": 1,
"creditsRemaining": 4999,
"freshness": "fresh"
},
"quality": {
"confidence": 0.94,
"complete": true,
"blocked": false
},
"markdown": "<string>",
"html": "<string>",
"rawHtml": "<string>",
"links": {
"internal": [
"<string>"
],
"external": [
"<string>"
]
},
"structured": {
"entities": [
{
"type": "Product",
"properties": {}
}
],
"jsonLd": [
{}
],
"microdata": [
{}
],
"opengraph": [
{}
],
"rdfa": [
{}
],
"appState": [
"<string>"
]
},
"summary": "<string>",
"json": {}
}
}{
"jobId": "019f3c09-6fae-740f-9257-10c2b6af7f43",
"status": "queued"
}