CrawlMe API
Submit URLs for indexing, check their status and track whole batches from your own code. One API key works with both API versions, against the same account, balance and submission history.
Choose a version
API v2 Recommended
The latest API, for new integrations. Clean URLs, one consistent response shape, machine-readable error codes, batch tracking and safe retries.
- Batch summary and per-batch submission list
- Optional
Idempotency-Keyheader for retry-safe submissions - Specific error codes with HTTP statuses that match (402, 409, 422, 429, 503)
- Rate-limit headers and
Retry-After - ISO 8601 timestamps, accepted/invalid/duplicate counts, remaining balance
API v1 Supported
The original API. Existing integrations keep working exactly as they do today, with the same keys, paths, fields and limits. Nothing needs to change.
- Balance, submit, one submission, paginated list
- Same account, balance and submission records as v2
- Same 60 requests/minute account limit, shared with v2
Quick start
Get an API key from your dashboard (API Access in the sidebar; an Admin may need to enable API access for your account first). Then:
1. Check your balance
curl -X GET "https://crawlme.net/api/v2/balance" \
-H "Authorization: Bearer YOUR_API_KEY"<?php
$ch = curl_init("https://crawlme.net/api/v2/balance");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($response['success']) {
print_r($response['data']);
} else {
echo $status . ' ' . $response['error']['code'] . ': ' . $response['error']['message'];
}import requests
response = requests.get(
"https://crawlme.net/api/v2/balance",
headers={
"Authorization": "Bearer YOUR_API_KEY",
},
timeout=30,
)
body = response.json()
if body["success"]:
print(body["data"])
else:
print(response.status_code, body["error"]["code"], body["error"]["message"])const response = await fetch("https://crawlme.net/api/v2/balance", {
method: "GET",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
},
});
const body = await response.json();
if (body.success) {
console.log(body.data);
} else {
console.error(response.status, body.error.code, body.error.message);
}{
"success": true,
"data": {
"account_id": 1042,
"balance": 150.0
}
}2. Submit URLs
Send at least 1 URLs. Add an Idempotency-Key so a retried request can never be charged twice.
curl -X POST "https://crawlme.net/api/v2/submissions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: order-7f3a9c" \
-H "Content-Type: application/json" \
-d '{"urls":["https://example.com/page-1","https://example.com/page-2"]}'<?php
$ch = curl_init("https://crawlme.net/api/v2/submissions");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Idempotency-Key: order-7f3a9c',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['urls' => ['https://example.com/page-1', 'https://example.com/page-2']]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($response['success']) {
print_r($response['data']);
} else {
echo $status . ' ' . $response['error']['code'] . ': ' . $response['error']['message'];
}import requests
response = requests.post(
"https://crawlme.net/api/v2/submissions",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Idempotency-Key": "order-7f3a9c",
},
json={"urls":["https://example.com/page-1","https://example.com/page-2"]},
timeout=30,
)
body = response.json()
if body["success"]:
print(body["data"])
else:
print(response.status_code, body["error"]["code"], body["error"]["message"])const response = await fetch("https://crawlme.net/api/v2/submissions", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Idempotency-Key": "order-7f3a9c",
"Content-Type": "application/json",
},
body: JSON.stringify({"urls":["https://example.com/page-1","https://example.com/page-2"]}),
});
const body = await response.json();
if (body.success) {
console.log(body.data);
} else {
console.error(response.status, body.error.code, body.error.message);
}{
"success": true,
"data": {
"batch_id": 293244,
"replayed": false,
"accepted_count": 2,
"invalid_count": 0,
"duplicate_count": 0,
"credit_cost": 2.0,
"remaining_balance": 148.0,
"invalid": [],
"duplicates": [],
"submission_ids": [
8812001,
8812002
],
"batch": {
"id": 293244,
"account_id": 1042,
"status": "PENDING",
"total": 2,
"successful": 0,
"failed": 0,
"pending": 2,
"queued": 2,
"processing": 0,
"credit_cost": 2.0,
"created_at": "2026-10-23T09:15:02Z",
"completed_at": null
}
}
}3. Track the batch
curl -X GET "https://crawlme.net/api/v2/batches/293244" \
-H "Authorization: Bearer YOUR_API_KEY"<?php
$ch = curl_init("https://crawlme.net/api/v2/batches/293244");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($response['success']) {
print_r($response['data']);
} else {
echo $status . ' ' . $response['error']['code'] . ': ' . $response['error']['message'];
}import requests
response = requests.get(
"https://crawlme.net/api/v2/batches/293244",
headers={
"Authorization": "Bearer YOUR_API_KEY",
},
timeout=30,
)
body = response.json()
if body["success"]:
print(body["data"])
else:
print(response.status_code, body["error"]["code"], body["error"]["message"])const response = await fetch("https://crawlme.net/api/v2/batches/293244", {
method: "GET",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
},
});
const body = await response.json();
if (body.success) {
console.log(body.data);
} else {
console.error(response.status, body.error.code, body.error.message);
}Poll every few minutes until pending reaches 0. The v2 reference explains every field and status.
How it works
- Validation. Each URL must be a well-formed
httporhttpsURL of at most 2048 characters. Invalid entries are returned to you and skipped. - Duplicate detection. A URL repeated in the same request, or submitted by your account in the last 1 minutes, is skipped and listed as a duplicate. Nothing is charged for it.
- Charging. Accepted URLs cost 1 credit each, deducted up front in one atomic step. If your balance does not cover all accepted URLs, nothing is charged and nothing is submitted.
- Queueing. Accepted URLs are stored as
PENDINGsubmissions and handed to an indexing provider by a background worker within a few minutes. The API never waits on a provider. - Status. Submissions move to
PROCESSINGonce a provider accepts them, thenSUCCESSorFAILED. Failed submissions are refunded automatically.
What SUCCESS means. The indexing provider accepted and processed the URL. CrawlMe does not verify whether Google has indexed the page; indexing is decided by search engines and can take time or not happen at all.