Cancel a crawl
curl --request DELETE \
--url https://api.prefetch.io/crawl/{id} \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.prefetch.io/crawl/{id}"
headers = {"X-API-Key": "<api-key>"}
response = requests.delete(url, headers=headers)
print(response.text)const options = {method: 'DELETE', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.prefetch.io/crawl/{id}', 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.prefetch.io/crawl/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_HTTPHEADER => [
"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"
"net/http"
"io"
)
func main() {
url := "https://api.prefetch.io/crawl/{id}"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.delete("https://api.prefetch.io/crawl/{id}")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.prefetch.io/crawl/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Delete.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": "<string>",
"url": "<string>",
"total": 123,
"completed": 123,
"failed": 123,
"credits_used": 123,
"created_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"finished_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z",
"error": "<string>",
"next": "<string>",
"data": [
{
"url": "<string>",
"depth": 123,
"status_code": 123,
"ok": true,
"error": "<string>",
"final_url": "<string>",
"domain": "stripe.com",
"metadata": {
"source_url": "<string>",
"final_url": "<string>",
"status_code": 123,
"title": "Stripe | Financial Infrastructure",
"description": "<string>",
"language": "en",
"canonical_url": "<string>",
"favicon": "<string>",
"author": "<string>",
"site_name": "<string>",
"published_time": "<string>",
"modified_time": "<string>",
"robots": "<string>",
"keywords": [
"<string>"
],
"open_graph": {},
"twitter": {},
"json_ld": [
{}
],
"headings": [
{
"level": 123,
"text": "<string>"
}
]
},
"markdown": {
"content": "<string>",
"char_count": 123,
"word_count": 123,
"truncated": true,
"main_content_only": true
},
"summary": {
"text": "<string>",
"model": "<string>"
},
"html": {
"content": "<string>",
"char_count": 123,
"truncated": true,
"main_content_only": true
},
"raw_html": {
"content": "<string>",
"char_count": 123,
"truncated": true,
"main_content_only": true
},
"links": {
"internal": [
{
"url": "<string>",
"text": "<string>",
"title": "<string>",
"rel": "<string>"
}
],
"external": [
{
"url": "<string>",
"text": "<string>",
"title": "<string>",
"rel": "<string>"
}
],
"count": 123
},
"images": {
"assets": [
{
"url": "<string>",
"alt": "<string>",
"width": 123,
"height": 123,
"loading": "<string>"
}
],
"og_image": "<string>",
"twitter_image": "<string>",
"count": 123
},
"json": {
"data": {},
"model": "<string>"
},
"warnings": [
"<string>"
]
}
]
},
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}Web extraction
DELETE /crawl/{id}
Stop a running crawl. Pages already fetched stay readable. Free.
DELETE
/
crawl
/
{id}
Cancel a crawl
curl --request DELETE \
--url https://api.prefetch.io/crawl/{id} \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.prefetch.io/crawl/{id}"
headers = {"X-API-Key": "<api-key>"}
response = requests.delete(url, headers=headers)
print(response.text)const options = {method: 'DELETE', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.prefetch.io/crawl/{id}', 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.prefetch.io/crawl/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_HTTPHEADER => [
"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"
"net/http"
"io"
)
func main() {
url := "https://api.prefetch.io/crawl/{id}"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.delete("https://api.prefetch.io/crawl/{id}")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.prefetch.io/crawl/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Delete.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": "<string>",
"url": "<string>",
"total": 123,
"completed": 123,
"failed": 123,
"credits_used": 123,
"created_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"finished_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z",
"error": "<string>",
"next": "<string>",
"data": [
{
"url": "<string>",
"depth": 123,
"status_code": 123,
"ok": true,
"error": "<string>",
"final_url": "<string>",
"domain": "stripe.com",
"metadata": {
"source_url": "<string>",
"final_url": "<string>",
"status_code": 123,
"title": "Stripe | Financial Infrastructure",
"description": "<string>",
"language": "en",
"canonical_url": "<string>",
"favicon": "<string>",
"author": "<string>",
"site_name": "<string>",
"published_time": "<string>",
"modified_time": "<string>",
"robots": "<string>",
"keywords": [
"<string>"
],
"open_graph": {},
"twitter": {},
"json_ld": [
{}
],
"headings": [
{
"level": 123,
"text": "<string>"
}
]
},
"markdown": {
"content": "<string>",
"char_count": 123,
"word_count": 123,
"truncated": true,
"main_content_only": true
},
"summary": {
"text": "<string>",
"model": "<string>"
},
"html": {
"content": "<string>",
"char_count": 123,
"truncated": true,
"main_content_only": true
},
"raw_html": {
"content": "<string>",
"char_count": 123,
"truncated": true,
"main_content_only": true
},
"links": {
"internal": [
{
"url": "<string>",
"text": "<string>",
"title": "<string>",
"rel": "<string>"
}
],
"external": [
{
"url": "<string>",
"text": "<string>",
"title": "<string>",
"rel": "<string>"
}
],
"count": 123
},
"images": {
"assets": [
{
"url": "<string>",
"alt": "<string>",
"width": 123,
"height": 123,
"loading": "<string>"
}
],
"og_image": "<string>",
"twitter_image": "<string>",
"count": 123
},
"json": {
"data": {},
"model": "<string>"
},
"warnings": [
"<string>"
]
}
]
},
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"success": false,
"error": "Credit limit exceeded",
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}Overview
Stops a crawl that ispending or running. The worker notices within a few pages and stops fetching.
Nothing is thrown away: pages already crawled stay readable through GET /crawl/{id} until the crawl expires, 24 hours after it stopped. They are billed on the next status call as usual, so you pay for what was fetched and nothing more.
Cancelling is free.
Example request
curl -X DELETE "https://api.prefetch.io/crawl/crw_9f2c1a7b4e0d4c3a8b1e5f7d2c9a0b3e" \
-H "X-API-Key: $PREFETCH_API_KEY"
const res = await fetch(
`https://api.prefetch.io/crawl/${crawlId}`,
{ method: "DELETE", headers: { "X-API-Key": process.env.PREFETCH_API_KEY } }
);
const { data } = await res.json();
console.log(data.status); // "cancelled"
import requests, os
r = requests.delete(
f"https://api.prefetch.io/crawl/{crawl_id}",
headers={"X-API-Key": os.environ["PREFETCH_API_KEY"]},
)
print(r.json()["data"]["status"]) # "cancelled"
Example response
{
"success": true,
"data": {
"id": "crw_9f2c1a7b4e0d4c3a8b1e5f7d2c9a0b3e",
"status": "cancelled",
"url": "https://docs.stripe.com",
"total": 38,
"completed": 12,
"failed": 1,
"credits_used": 36,
"created_at": "2026-08-20T09:14:03.221Z",
"started_at": "2026-08-20T09:14:04.010Z",
"finished_at": "2026-08-20T09:16:41.882Z",
"expires_at": "2026-08-21T09:14:03.221Z",
"error": null
},
"meta": {
"requestId": "e7b6a5f4-bd0e-4f70-c15c-4d6e8f0a2b43",
"durationMs": 61
}
}
Behavior
- Already finished? Cancelling a
completed,failed, orcancelledcrawl is a no-op. You get200with the crawl’s real final state rather than an error. - Not yet started? The queued job is removed, so no pages are ever fetched.
- Unknown or expired id?
404.
404 rather than 403. Crawl ids are unguessable, and confirming that some id exists is the only thing an enumeration attempt is after.
Related
POST /crawl
Start a crawl.
GET /crawl/{id}
Read progress and results.
Authorizations
Your Prefetch API key. Obtain one from the dashboard.
Path Parameters
The crawl id returned by POST /crawl.
Example:
"crw_9f2c1a7b4e0d4c3a8b1e5f7d2c9a0b3e"
⌘I