Configure Squid Chain
curl --request POST \
--url https://api.lobstr.io/v1/squids/{id}/chain \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"target_module_id": "<string>",
"field_map": {},
"autostart": true,
"cluster_name": "<string>",
"cluster_concurrency": 123
}
'import requests
url = "https://api.lobstr.io/v1/squids/{id}/chain"
payload = {
"target_module_id": "<string>",
"field_map": {},
"autostart": True,
"cluster_name": "<string>",
"cluster_concurrency": 123
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({
target_module_id: '<string>',
field_map: {},
autostart: true,
cluster_name: '<string>',
cluster_concurrency: 123
})
};
fetch('https://api.lobstr.io/v1/squids/{id}/chain', 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.lobstr.io/v1/squids/{id}/chain",
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([
'target_module_id' => '<string>',
'field_map' => [
],
'autostart' => true,
'cluster_name' => '<string>',
'cluster_concurrency' => 123
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$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.lobstr.io/v1/squids/{id}/chain"
payload := strings.NewReader("{\n \"target_module_id\": \"<string>\",\n \"field_map\": {},\n \"autostart\": true,\n \"cluster_name\": \"<string>\",\n \"cluster_concurrency\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
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.lobstr.io/v1/squids/{id}/chain")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"target_module_id\": \"<string>\",\n \"field_map\": {},\n \"autostart\": true,\n \"cluster_name\": \"<string>\",\n \"cluster_concurrency\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.lobstr.io/v1/squids/{id}/chain")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"target_module_id\": \"<string>\",\n \"field_map\": {},\n \"autostart\": true,\n \"cluster_name\": \"<string>\",\n \"cluster_concurrency\": 123\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"target_module": {
"id": "<string>",
"public_name": "<string>",
"max_concurrency": 123
},
"target_cluster": {},
"field_map": {},
"is_active": true,
"autostart": true,
"cluster_name": {},
"cluster_concurrency": {},
"created_at": "<string>"
}Squid
Configure Squid Chain
Chain two crawlers together so results from one automatically feed into another
POST
/
v1
/
squids
/
{id}
/
chain
Configure Squid Chain
curl --request POST \
--url https://api.lobstr.io/v1/squids/{id}/chain \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"target_module_id": "<string>",
"field_map": {},
"autostart": true,
"cluster_name": "<string>",
"cluster_concurrency": 123
}
'import requests
url = "https://api.lobstr.io/v1/squids/{id}/chain"
payload = {
"target_module_id": "<string>",
"field_map": {},
"autostart": True,
"cluster_name": "<string>",
"cluster_concurrency": 123
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({
target_module_id: '<string>',
field_map: {},
autostart: true,
cluster_name: '<string>',
cluster_concurrency: 123
})
};
fetch('https://api.lobstr.io/v1/squids/{id}/chain', 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.lobstr.io/v1/squids/{id}/chain",
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([
'target_module_id' => '<string>',
'field_map' => [
],
'autostart' => true,
'cluster_name' => '<string>',
'cluster_concurrency' => 123
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$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.lobstr.io/v1/squids/{id}/chain"
payload := strings.NewReader("{\n \"target_module_id\": \"<string>\",\n \"field_map\": {},\n \"autostart\": true,\n \"cluster_name\": \"<string>\",\n \"cluster_concurrency\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
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.lobstr.io/v1/squids/{id}/chain")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"target_module_id\": \"<string>\",\n \"field_map\": {},\n \"autostart\": true,\n \"cluster_name\": \"<string>\",\n \"cluster_concurrency\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.lobstr.io/v1/squids/{id}/chain")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"target_module_id\": \"<string>\",\n \"field_map\": {},\n \"autostart\": true,\n \"cluster_name\": \"<string>\",\n \"cluster_concurrency\": 123\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"target_module": {
"id": "<string>",
"public_name": "<string>",
"max_concurrency": 123
},
"target_cluster": {},
"field_map": {},
"is_active": true,
"autostart": true,
"cluster_name": {},
"cluster_concurrency": {},
"created_at": "<string>"
}Crawler chaining lets you connect two squids so that when a run on the source squid completes, its results are automatically extracted and queued as tasks on a target squid, triggering a new run downstream.
Example use case: Run a Google Maps Leads scrape to collect place URLs, then automatically pass those URLs into a Google Maps Reviews scrape — without any manual intervention.
Chains are limited to a maximum depth of 3 squids. A squid can only have one outgoing chain at a time.
How it works
- You configure a chain on a source squid, specifying the target crawler and a
field_map - When a run on the source squid completes with a status of
DONE, the scheduler extracts the mapped fields from its results - Those values are queued as tasks on the target squid (auto-created on first trigger if it doesn’t exist yet)
- If
autostartistrue, a new run starts immediately on the target squid
The chain is only triggered when the source run reaches a status of
DONE. Runs that end with ERROR or any other status will not trigger the downstream chain.Path Parameters
string
required
Hash ID of the source squid. Example:
"e86b29c032024b66aff529e1d43c2bd7"Headers
string
required
Your API authentication token. Value:
Token YOUR_API_KEYstring
required
Must be application/json. Value:
application/jsonRequest Body
string
required
Hash ID of the target crawler. Use
GET /v1/crawlers to find it. Example: "9a3f1c8b2e7d4a56f0e1b2c3d4e5f678"object
required
Maps a result field from the source crawler to an input parameter on the target crawler. Example:
{"url": "url"} — passes the url field from source results as the url input on the target.boolean
If
true, the downstream run starts automatically when tasks are queued. If false, tasks are queued and the run is created as paused for manual review. Defaults to true.string
Custom name for the auto-created target squid. If omitted, defaults to
"{Crawler Name} (N) (chained)".integer
Concurrency to apply to the auto-created target squid. Defaults to
1.Response Fields
string
Unique chain config identifier.
object
object | null
The auto-created target squid.
null until the first chain trigger fires.object
The configured field mapping.
boolean
Whether the chain is currently active.
boolean
Whether downstream runs start automatically.
string | null
Configured name for the target squid.
integer | null
Configured concurrency for the target squid.
string
ISO 8601 timestamp of when the chain was configured.
Code Examples
curl -X POST "https://api.lobstr.io/v1/squids/e86b29c032024b66aff529e1d43c2bd7/chain" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target_module_id": "9a3f1c8b2e7d4a56f0e1b2c3d4e5f678",
"field_map": {"url": "url"},
"autostart": true,
"cluster_name": "Reviews (chained)",
"cluster_concurrency": 3
}'
import requests
url = "https://api.lobstr.io/v1/squids/e86b29c032024b66aff529e1d43c2bd7/chain"
headers = {
"Authorization": "Token YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"target_module_id": "9a3f1c8b2e7d4a56f0e1b2c3d4e5f678",
"field_map": {"url": "url"},
"autostart": True,
"cluster_name": "Reviews (chained)",
"cluster_concurrency": 3
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
Response
201
{
"id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"target_module": {
"id": "9a3f1c8b2e7d4a56f0e1b2c3d4e5f678",
"public_name": "Google Maps Reviews Scraper",
"name": "googlemapreviews_matrix",
"max_concurrency": 10
},
"target_cluster": null,
"field_map": {"url": "url"},
"is_active": true,
"autostart": true,
"cluster_name": "Reviews (chained)",
"cluster_concurrency": 3,
"created_at": "2026-04-28T10:00:00Z"
}
400
{
"error": "field_map is required and must be a dict."
}
409
{
"error": "This squid already has a chain configured."
}
422
{
"error": "Chain depth limit of 3 reached."
}