Forms: one tool, three modes
Read a form before you touch it, send an application, or send it and solve the captcha on the way. The mode is a parameter, not a separate endpoint.
Overview
Sending a form blind is how people get blocked, accused of spam, or post an empty request to a client. So the tool starts in the safest mode and only does more when you ask.
| mode | What happens | Price / 1,000 |
|---|---|---|
inspect (default) |
Finds the forms on the page and returns their schema: action, method, every field with its HTML type, required flags, choices, hidden CSRF fields — and a captcha verdict with the recognised type and sitekey. Nothing is submitted. | $1.00 |
submit |
Sends one form with the values you provide, then checks whether it was actually accepted (success phrase, changed URL, or JSON echo of the fields). | $2.00 |
submit_captcha |
Same as submit, but solves the captcha first and injects the token
into the field the form expects. The captcha solution is billed as a
separate line — it costs about ten times the form call, so you see it. |
$2.00 + captcha |
What we refuse to send — in code, not in policy text
- Password fields — that is a login into someone's account, not an application.
- Payment fields (
card,cvv,iban,amount,price) — we do not move money for anyone. - File uploads — requires the site owner's consent, which we cannot verify.
- Cloudflare Challenge — needs a browser session and cookies, not a form field. Refused honestly rather than half-done.
Fields you pass that are not part of the form are ignored, and so is a reset button.
Hidden fields (CSRF tokens) are forwarded exactly as the page provides them. Missing required
fields are rejected before anything is sent (and before any captcha is paid for).
curl -X POST https://api.serp-base.com/v1/query \
-H "Content-Type: application/json" \
-d '{"tool": "form_action", "query": "https://example.com/contact", "mode": "inspect"}'
Parameters
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
url | string | yes | Page with the form (absolute http/https URL). query is an alias. | https://example.com/contact |
mode | string | no | inspect (default) · submit · submit_captcha. | submit |
values | object | for submit | Field name to value. Only fields that exist in the form are sent. | {"name": "Ivan"} |
form_index | int | no | Which form on the page (default 0). Use mode=inspect to list them. | 0 |
robots | bool | no | Respect robots.txt. Default true. | true |
Code examples
Read the form first (recommended)
import requests
BASE = "https://api.serp-base.com/v1/query"
# 1) read โ finds fields, required flags and any captcha
r = requests.post(BASE, json={
"tool": "form_action",
"query": "https://example.com/contact",
"mode": "inspect",
})
data = r.json()["data"]
print(data["forms_found"], "forms, captcha:", data["captcha"])
for form in data["forms"]:
print(form["method"], form["action"], "submit_allowed:", form["submit_allowed"])
for f in form["fields"]:
print(" ", f["name"], f["type"], "required" if f.get("required") else "")
# 2) send โ only once you know what the form wants
r = requests.post(BASE, json={
"tool": "form_action",
"query": "https://example.com/contact",
"mode": "submit",
"values": {"name": "Ivan", "email": "[email protected]", "message": "Hello"},
})
print(r.json()["data"]["status"])
cURL — submit with captcha
curl -X POST https://api.serp-base.com/v1/query \
-H "Content-Type: application/json" \
-d '{"tool": "form_action", "query": "https://example.com/contact",
"mode": "submit_captcha",
"values": {"name": "Ivan", "email": "[email protected]", "message": "Hello"}}'
JavaScript
const r = await fetch('https://api.serp-base.com/v1/query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tool: 'form_action',
query: 'https://example.com/contact',
mode: 'inspect',
}),
});
const { data } = await r.json();
console.log(data.forms_found, data.captcha);
PHP
$ch = curl_init('https://api.serp-base.com/v1/query');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'tool' => 'form_action',
'query' => 'https://example.com/contact',
'mode' => 'inspect',
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response
mode=inspect — the form schema, nothing sent:
{
"ok": true,
"data": {
"url": "https://example.com/contact",
"forms_found": 1,
"forms": [
{ "index": 0, "action": "https://example.com/contact", "method": "POST",
"fields_count": 4, "submit_allowed": true, "blocked_reason": null,
"fields": [
{ "name": "name", "type": "text", "required": true },
{ "name": "email", "type": "email", "required": true },
{ "name": "message", "type": "textarea", "required": true },
{ "name": "csrf_token", "type": "hidden", "value_prefill": "a1b2c3" }
] }
],
"captcha": null,
"submit_requires_captcha": false,
"note": "This is READ mode: nothing was submitted."
}
}
mode=submit — the verdict:
{
"ok": true,
"data": {
"status": "submitted",
"http_status": 200,
"final_url": "https://example.com/thanks",
"sent_fields": ["csrf_token", "email", "message", "name"],
"ignored_fields": [],
"detail": "success marker found in the response"
}
}
mode=submit_captcha — adds the captcha and a transparent price split:
{
"ok": true,
"data": {
"status": "submitted",
"captcha": { "type": "recaptcha", "token_field": "g-recaptcha-response", "token_length": 2510 },
"cost_breakdown": {
"form_call": 0.19,
"captcha_solution": 1.9,
"note": "captcha solution is billed as a separate line - it costs ~10x the form call"
}
}
}
status values: submitted (a real confirmation signal was found),
submitted_rejected (the response carries a rejection marker),
submitted_unconfirmed (HTTP 200 but no proof either way — not charged).
Payment
| mode | Price / 1,000 | Charged when |
|---|---|---|
inspect | $1.00 | the page was read and the schema returned |
submit | $2.00 | the submission is confirmed |
submit_captcha | $2.00 + captcha | confirmed + the captcha solution, shown separately |
Refusals (password, payment, captcha refused, missing required fields) are free and carry a reason. Reading costs less than sending because it does less.
Errors
| Code | Meaning | Action |
|---|---|---|
402 | Payment required | Sign the payment and retry with X-PAYMENT |
400 | Bad parameters | Check url, mode, values |
422 | Refused or unconfirmed | The error text explains why — nothing charged |
503 | Tool temporarily unavailable | Retry later — nothing charged |
Related tools
Scraping: URL to data · Captcha: reCAPTCHA V2 · Extract data by schema
Proxy routing
Requests to the target site never leave from our infrastructure address. Each call is routed
through a proxy: your own if you set one, a proxy you buy from us, or our system proxy which is
included and needs no setup. Set your own once with
POST /v1/proxy/own, or buy a dedicated IPv4 by country and period via
POST /v1/proxy/buy. See Proxies.