Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
An introduction to the Gaffa Browser API. Learn how you can get started building fast, powerful web automations!
An example request that uses Gaffa to convert an HTML page to a PDF. There are lots of HMTL to PDF API's but Gaffa handles it easily, as well as doing much more.
"actions": [
{
"type": "capture_dom"
}
]{
"url": "https://demo.gaffa.dev/simulate/table?loadTime=3&rowCount=20",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"actions": [
{
"type": "wait",
"selector": "table"
},
{
"type": "print",
"size": "A4",
"margin": 20,
"orientation": "portrait"
}
]
}
}An example request that uses Gaffa to convert a web page page to markdown. This could be used to export web page reports or to print the content of a page in a readable format.
An example request that uses Gaffa to dismiss a modal, scroll to the bottom of a page and then capture a full height screenshot.

{
"url": "https://demo.gaffa.dev/simulate/table?loadTime=3&rowCount=20",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"max_media_bandwidth": null,
"actions": [
{
"type": "wait",
"selector": "table"
},
{
"type": "print",
"size": "A4",
"margin": 20,
"orientation": "portrait"
}
]
}
}"actions": [
{
"type": "download_file",
"timeout": 20000
}
]"actions": [
{
"id": "act_VHhrUbXjZSaYCPTqbBYD4acCzzeFGH",
"type": "download_file",
"query": "download_file?continue_on_fail=false&timeout=20000",
"timestamp": "2025-05-30T15:02:06.6615306Z",
"output": "https://storage.gaffa.dev/brq/downloads/5845df07-3749-424e-9c64-9602be19a857.pdf"
}
]"actions": [
{
"type": "generate_simplified_dom"
}
]"actions": [
{
"type": "wait",
"time": 1000,
}
]"actions": [
{
"type": "wait",
"selector": "table",
"timeout": 5000,
"continueOnFail": true
}
]"actions": [
{
"type": "print",
"page_size": "A4",
"orientation": "landscape",
"margin": 20
}
]"actions": [
{
"type": "click",
"selector": "a.header__logo"
}
]"actions": [
{
"type": "wait",
"selector": "a.header__logo",
"timeout": 5000,
"continueOnFail": true
}
]"actions": [
{
"name": "type",
"selector": "#postform-text",
"text": "Hello world!"
}
]"actions": [
{
"name": "type",
"selector": "form input[name="email"]",
"text": "test@test.com"
"timeout": 10000
}
]{
"url": "https://demo.gaffa.dev/simulate/ecommerce?loadTime=3&showModal=true&modalDelay=0&itemCount=20",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"actions": [
{
"type": "wait",
"selector": "div[role=\"dialog\"]",
"timeout": 10000
},
{
"type": "click",
"selector": "[data-testid=\"accept-all-button\"]"
},
{
"type": "wait",
"selector": "[data-testid^=\"product-1\"]",
"timeout": 5000
},
{
"type": "scroll",
"percentage": 100
},
{
"type": "capture_screenshot",
"size": "fullscreen"
}
]
}
}{
"url": "https://demo.gaffa.dev/simulate/article?loadTime=3¶graphs=10&images=3",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"actions": [
{
"type": "wait",
"selector": "article"
},
{
"type": "generate_markdown"
}
]
}
}"actions": [
{
"type": "capture_snapshot",
}
]"actions": [
{
"type": "capture_screenshot",
"size": "view"
}
]"actions": [
{
"type": "capture_element",
"selector": ".page_contents",
"timeout": 1000
}
]An example request that uses Gaffa to extract structured data (JSON) from a table on a webpage
{
"url": "https://demo.gaffa.dev/simulate/table?loadTime=1&rowCount=3",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"actions": [
{
"type": "wait",
"selector": "table",
"timeout": 5000
},
{
"type": "parse_table",
"selector": "table"
}
]
}
}{
"data": {
"id": "brq_abc123ExampleRequestId",
"url": "https://demo.gaffa.dev/simulate/table?loadTime=1&rowCount=10",
"state": "completed",
"credit_usage": 1,
"http_status_code": 200,
"from_cache": false,
"started_at": "2025-06-09T12:00:00.000Z",
"completed_at": "2025-06-09T12:00:04.321Z",
"running_time": "00:00:04.3210000",
"page_load_time": "00:00:01.1230000",
"actions": [
{
"id": "act_wait001",
"type": "wait",
"query": "wait?selector=table&timeout=5000&continue_on_fail=false",
"timestamp": "2025-06-09T12:00:01.500Z"
},
{
"id": "act_parse001",
"type": "parse_table",
"query": "parse_table?selector=table",
"timestamp": "2025-06-09T12:00:01.600Z",
"output": "https://storage.gaffa.dev/brq/results/brq_abc123ExampleRequestId/act_parse001_table.json"
}
]
}
}An example request that uses Gaffa to automate the completion of a form and waits for a success modal to appear.
{
"url": "https://demo.gaffa.dev/simulate/form?loadTime=3&showModal=false&modalDelay=0&formType=address&firstName=John&lastName=Doe&address1=123%20Main%20Street&city=London&country=UK",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": true,
"actions": [
{
"type": "type",
"selector": "#email",
"text": "johndoe@example.com"
},
{
"type": "type",
"selector": "#state",
"text": "CA"
},
{
"type": "type",
"selector": "#zipCode",
"text": "12345"
},
{
"type": "click",
"selector": "button[type='submit']"
},
{
"type": "wait",
"selector": "[role=\"dialog\"] h2:has-text(\"Success!\")",
"timeout": 10000
}
]
}
}
[
{
"id": "1",
"name": "Item 1",
"quantity": "30",
"price": "$56.05"
},
{
"id": "2",
"name": "Item 2",
"quantity": "68",
"price": "$76.89"
},
{
"id": "3",
"name": "Item 3",
"quantity": "67",
"price": "$20.44"
}
]{
"url": "https://gaffa.dev",
"max_cache_age": 10000
}{
"data": {
"id": "smr_VQW4E66TdcQFZfCs6qavgdowPj3Bzk",
"url": "https://gaffa.dev",
"state": "completed",
"credit_usage": 1,
"from_cache": true,
"started_at": "2025-08-22T11:05:43.328175Z",
"completed_at": "2025-08-22T11:05:47.857941Z",
"running_time": "00:00:04.5297660",
"links": [
"https://gaffa.dev",
"https://gaffa.dev/about",
"https://gaffa.dev/blog",
"https://gaffa.dev/blog/convert-any-web-page-to-llm-ready-markdown-using-gaffa",
"https://gaffa.dev/blog/how-to-extract-and-simplify-a-webpage-dom-with-gaffa",
"https://gaffa.dev/blog/printing-webpages-to-pdf-html-to-pdf-using-gaffa",
"https://gaffa.dev/docs",
"https://gaffa.dev/docs/api-reference/api-authentication",
....and so on
],
"link_count": 52
}
}"actions": [
{
"type": "generate_markdown"
}
]"actions": [
{
"type": "generate_markdown",
"selector": "article",
"output_type": "inline"
}
]An example request that uses Gaffa to infinitely scroll down a simulated ecommerce site whilst recording the interaction.
An example request that uses Gaffa to extract structured data from an online PDF.
"actions": [
{
"type": "scroll",
"percentage": 50,
}
]"actions": [
{
"type": "scroll",
"percentage": 100,
"scroll_speed": "slow",
"max_scroll_time": 25000,
"interval": 1000,
"wait_time": 1000
}
]import os, time, requests, pathlib, urllib.parse
API_KEY = os.environ.get("GAFFA_API_KEY", "YOUR_API_KEY")
BASE = "https://api.gaffa.dev"
def submit_request(url, actions, async_mode=True):
payload = {
"url": url,
"async": async_mode,
"settings": {"actions": actions}
}
r = requests.post(
f"{BASE}/v1/browser/requests",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json=payload
)
r.raise_for_status()
return r.json()["data"]
def wait_for_completion(request_id, poll_every=2, max_wait=180):
start = time.time()
while True:
r = requests.get(
f"{BASE}/v1/browser/requests/{request_id}",
headers={"X-API-Key": API_KEY}
)
data = r.json()["data"]
if data["state"] in ("completed", "failed"):
return data
if time.time() - start > max_wait:
raise TimeoutError("Request timed out")
time.sleep(poll_every)
def download_outputs(brq, dest="outputs"):
dest = pathlib.Path(dest)
dest.mkdir(parents=True, exist_ok=True)
files = []
for act in brq.get("actions") or []:
out = act.get("output")
if isinstance(out, str) and out.startswith("http"):
name = pathlib.Path(urllib.parse.urlparse(out).path).name
p = dest / name
with requests.get(out, stream=True) as r:
with open(p, "wb") as f:
for chunk in r.iter_content(8192):
if chunk: f.write(chunk)
files.append(str(p))
return files
if __name__ == "__main__":
target_url = "https://demo.gaffa.dev/simulate/article?paragraphs=5"
actions = [
{"type": "wait", "selector": "article"},
{"type": "generate_markdown"}
]
job = submit_request(target_url, actions)
brq = wait_for_completion(job["id"])
print("Final state:", brq["state"])
if brq["state"] == "completed":
saved = download_outputs(brq)
print("Downloaded:", saved)python gaffa_script.py{
"url": "https://example.com",
"settings": {
"actions": [
{
"type": "parse_table",
"selector": ".large_table",
"timeout": 1000
}
]
}
}{
"url": "https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal)",
"settings": {
"actions": [
{
"type": "parse_table",
"selector": ".wikitable",
"timeout": 5000
}
]
}
}[
{
"country_territory": "United States",
"imf_2026": "30,337",
"imf_year": "2026",
"world_bank_2023": "27,361",
"world_bank_year": "2023"
},
{
"country_territory": "China",
"imf_2026": "19,534",
"imf_year": "2026",
"world_bank_2023": "17,795",
"world_bank_year": "2023"
}
]{
"url": "https://demo.gaffa.dev/simulate/pdf/ReasoningAboutActionAndChange.pdf",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"actions": [
{
"type": "download_file"
},
{
"type": "parse_json",
"data_schema": {
"name": "AcademicPaper",
"description": "Schema for parsing academic paper summary and author information",
"fields": [
{
"type": "string",
"name": "title",
"description": "The full title of the academic paper"
},
{
"type": "string",
"name": "abstract",
"description": "The paper's abstract or summary"
},
{
"type": "array",
"name": "authors",
"description": "List of authors who contributed to the paper",
"fields": [
{
"type": "string",
"name": "name",
"description": "Author's full name as it appears in the paper"
},
{
"type": "array",
"name": "affiliations",
"description": "Institutional affiliations for this author",
"fields": [
{
"type": "string",
"name": "institution",
"description": "Name of the university or research institution"
},
{
"type": "string",
"name": "department",
"description": "Department or division name"
},
{
"type": "string",
"name": "city",
"description": "City where the institution is located"
},
{
"type": "string",
"name": "country",
"description": "Country of the institution"
}
]
},
{
"type": "string",
"name": "email",
"description": "Author's contact email address if provided"
}
]
},
{
"type": "array",
"name": "keywords",
"description": "Key terms and topics covered in the paper",
"fields": [
{
"type": "string",
"name": "keyword",
"description": "Individual keyword or phrase"
}
]
}
]
},
"instruction": "Parse this academic paper focusing on the title, abstract, author information, and keywords typically found on the first page. Extract all author names, their institutional affiliations with department and location details, and their contact information.",
"model": "gpt-4o-mini",
"output_type": "inline",
"max_pages": 1
}
]
}
}{
"data": {
"id": "brq_VYfyVifa26oMpmX4YDeNN3iJDrhK3a",
"url": "https://demo.gaffa.dev/simulate/pdf/ReasoningAboutActionAndChange.pdf",
"state": "completed",
"credit_usage": 0,
"http_status_code": 200,
"from_cache": false,
"started_at": "2025-12-01T06:09:43.6125439Z",
"completed_at": "2025-12-01T06:09:57.5453161Z",
"running_time": "00:00:13.9327722",
"page_load_time": "00:00:00.8959680",
"actions": [
{
"id": "act_VYfyVhGPwQjur9XAu5XA47n2FozYfK",
"type": "download_file",
"timestamp": "2025-12-01T06:09:46.509484Z",
"output": "https://storage.gaffa.dev/brq/downloads/brq_VYfyVifa26oMpmX4YDeNN3iJDrhK3a/ReasoningAboutActionAndChange.pdf"
},
{
"id": "act_VYfyVjNHWzECbraio6xS6MqhYhiDWP",
"type": "parse_json",
"timestamp": "2025-12-01T06:09:57.5453056Z",
"output": {
"title": "Reasoning about Action and Change",
"abstract": "This chapter presents the state of research concerning the formalisation of an agent reasoning about a dynamic system which can be partially observed and acted upon. We first define the basic concepts of the area: system states, ontic and epistemic actions, observations; then the basic reasoning processes: prediction, progression, regression, postdiction, filtering, abduction, and extrapolation. We then recall the classical action representation problems and show how these problems are solved in some standard frameworks. For space reasons, we focus on these major settings: the situation calculus, STRIPS and some propositional action languages, dynamic logic, and dynamic Bayesian networks. We finally address a special case of progression, namely belief update.",
"authors": [
{
"name": "Florence Dupin de Saint-Cyr",
"affiliations": [
{
"institution": "IRIT-CNRS. Université Paul Sabatier",
"department": "",
"city": "Toulouse",
"country": "France"
}
],
"email": ""
},
{
"name": "Andreas Herzig",
"affiliations": [
{
"institution": "IRIT-CNRS. Université Paul Sabatier",
"department": "",
"city": "Toulouse",
"country": "France"
}
],
"email": ""
},
{
"name": "Jérôme Lang",
"affiliations": [
{
"institution": "CNRS, Université Paris-Dauphine, PSL Research University, LAMSADE",
"department": "",
"city": "Paris",
"country": "France"
}
],
"email": ""
},
{
"name": "Pierre Marquis",
"affiliations": [
{
"institution": "CRIL-CNRS, Université d’Artois & Institut Universitaire de France",
"department": "",
"city": "Lens",
"country": "France"
}
],
"email": ""
}
],
"keywords": []
},
"reference": "https://storage.gaffa.dev/brq/downloads/brq_VYfyVifa26oMpmX4YDeNN3iJDrhK3a/ReasoningAboutActionAndChange.pdf"
}
]
}
}{
"type": "", //the type of the action
//other params follow as key value pairs
"key": value //string, number, etc.
}{
"id": "", //a unique id given to the action by Gaffa
"type": "capture_screenshot", //the type of the action
"query": "", //a representation of the action in querystring format
"timestamp": "", //the UTC timestamp the action was executed
"output": "" //if the action has an output, you will find a URL for this here,
"error": "" //if the request fails, the error message will be returned here
}{
"url": "https://demo.gaffa.dev/simulate/ecommerce?loadTime=3&showModal=true&modalDelay=0&itemCount=infinite",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": true,
"actions": [
{
"type": "wait",
"selector": "div[role=\"dialog\"]",
"timeout": 10000
},
{
"type": "click",
"selector": "[data-testid=\"accept-all-button\"]"
},
{
"type": "wait",
"selector": "[data-testid^=\"product-1\"]",
"timeout": 5000
},
{
"type": "scroll",
"percentage": 100,
"max_scroll_time": 20000
}
]
}
}{
"url": "https://www.bbc.com/",
"settings": {
"max_media_bandwidth": 0,
"actions": [
{
"type": "generate_markdown"
}
]
}
}{
"url": "https://example.com",
"settings": {
"time_limit": 30000,
"actions": [...]
}
}{
"url": "https://www.allrecipes.com",
"settings": {
"block_ads": true,
"actions": [
{
"type": "capture_dom"
}
]
}
}{
"url": "https://example.com",
"settings": {
"actions": [
{
"type": "wait",
"selector": "table"
},
{
"type": "print",
"size": "A4",
"margin": 20,
"orientation": "portrait"
}
]
}
}{
"url": "https://www.bbc.com/",
"proxy_location": "us",
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"max_media_bandwidth": 0,
"time_limit": 60000,
"block_ads": true,
"actions": [
{
"type": "wait",
"selector": "table"
},
{
"type": "print",
"size": "A4",
"margin": 20,
"orientation": "portrait"
}
]
}
}{
"data": {
"id": "brq_VJX3mbESLiyCFYvZQEUih9RdDYovog",
"url": "https://en.wikipedia.org/wiki/Artificial_intelligence",
"proxy_location": null,
"state": "completed",
"credit_usage": 2,
"http_status_code": 200,
"from_cache": false,
"started_at": "2025-06-09T15:55:46.4235903Z",
"completed_at": "2025-06-09T15:56:27.9381332Z",
"running_time": "00:00:40.7348244",
"page_load_time": "00:00:02.2087117",
"actions": [
{
"id": "act_VJX3memaue6YUgFcn44uNscZbVUpYg",
"type": "wait",
"query": "wait?selector=%23cookie-policy-notice%2C%20.mw-cookie-consent-container&timeout=10000&continue_on_fail=true",
"timestamp": "2025-06-09T15:55:48.6323091Z",
"error": "action_timed_out"
},
{
"id": "act_VJX3mkwfwNPdGiMUpqKr34Tm5xzyUU",
"type": "click",
"query": "click?selector=%23cookie-policy-notice%20button%2C%20.mw-cookie-consent-container%20button&continue_on_fail=true&timeout=5000",
"timestamp": "2025-06-09T15:55:58.7949275Z",
"error": "action_timed_out"
},
{
"id": "act_VJX3mkSJ3sevWRXUCjFy6zwfD172fV",
"type": "wait",
"query": "wait?selector=%23firstHeading&timeout=10000&continue_on_fail=false",
"timestamp": "2025-06-09T15:56:03.9581113Z"
},
{
"id": "act_VJX3mbq9Jgj8EwADszW2AqdeJJXJiY",
"type": "scroll",
"query": "scroll?percentage=100&max_scroll_time=20000&scroll_speed=medium&continue_on_fail=false",
"timestamp": "2025-06-09T15:56:03.9691994Z"
},
{
"id": "act_VJX3mjBQYv8zTsXv1SkgUnBkzNFmJU",
"type": "capture_screenshot",
"query": "capture_screenshot?size=fullscreen&continue_on_fail=false",
"timestamp": "2025-06-09T15:56:20.0727905Z",
"output": "https://storage.gaffa.dev/brq/image/brq_VJX3mbESLiyCFYvZQEUih9RdDYovog/act_VJX3mjBQYv8zTsXv1SkgUnBkzNFmJU_full.png"
}
]
},
"error": null
}curl https://api.gaffa.dev/v1/browser/requests \
--request POST \
--header 'Content-Type: application/json' \
--header 'X-API-Key: YOUR_API_KEY' \
--data '{
"url": "https://en.wikipedia.org/wiki/Artificial_intelligence",
"async": false,
"max_cache_age": 0,
"settings": {
"actions": [
{
"type": "wait",
"selector": "#cookie-policy-notice",
"timeout": 10000,
"continue_on_fail": true
},
{
"type": "click",
"selector": "#cookie-policy-notice",
"continue_on_fail": true
},
{
"type": "wait",
"selector": "#firstHeading",
"timeout": 10000
},
{
"type": "scroll",
"percentage": 100
},
{
"type": "capture_screenshot",
"size": "fullscreen"
}
]
}
}'python -m venv venv && source venv/bin/activatepip install requests openaiGAFFA_API_KEY=your_gaffa_api_key
OPENAI_API_KEY=your_openai_api_keyimport requests
import openai
GAFFA_API_KEY = os.getenv("GAFFA_API_KEY")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
# Fetch the markdown content from Gaffa
def fetch_markdown_with_gaffa(url):
payload = {
"url": url,
"proxy_location": None,
"async": False,
"max_cache_age": 0,
"settings": {
"record_request": False,
"actions": [
{
"type": "wait",
"selector": "article"
},
{
"type": "generate_markdown"
}
]
}
}
# Set the headers for the request
headers = {
"x-api-key": GAFFA_API_KEY,
"Content-Type": "application/json"
}
# Make the POST request to the Gaffa API
print("Calling Gaffa API to generate markdown...")
response = requests.post("https://api.gaffa.dev/v1/browser/requests", json=payload, headers=headers)
response.raise_for_status()
# Extract the markdown URL from the response
markdown_url = response.json()["data"]["actions"][1]["output"]
# Fetch the markdown content from the generated URL
print(f"📥 Fetching markdown from: {markdown_url}")
markdown_response = requests.get(markdown_url)
markdown_response.raise_for_status()
return markdown_response.textdef ask_question(markdown, question):
openai.api_key = OPENAI_API_KEY
prompt = (
f"You are an assistant helping analyze different webpages.\n\n"
f"Markdown content:\n{markdown[:3000]}\n\n"
f"Question: {question}\nAnswer as clearly as possible."
)
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": prompt}
]
)
return response.choices[0].message["content"]def main():
url = input("Enter the URL of the article: ")
try:
markdown = fetch_markdown_with_gaffa(url)
print("\n✅ Markdown successfully retrieved from Gaffa.\n")
while True:
question = input("Ask a question about the content (or type 'exit'): ")
if question.lower() == "exit":
break
answer = ask_question(markdown, question)
print(f"\n💬 Answer: {answer}\n")
except Exception as e:
print(f"⚠️ Error: {e}")
if __name__ == "__main__":
main()python your_script_name.pyThis tutorial will show you how you can use Gaffa to retrieve all images from a site and then download all images across those pages.
An example request that uses Gaffa to analyze a web form and extract all input fields, their labels, types, and properties into structured JSON.
site/map{
"url": "https://demo.gaffa.dev/simulate/form?loadTime=3&showModal=true&modalDelay=5&formType=address",
"proxy_location": null,
"async": false,
"max_cache_age": 0,
"settings": {
"record_request": false,
"actions": [
{
"type": "parse_json",
"data_schema": {
"name": "AddressFormSchema",
"description": "Extracts fields, labels, and placeholders from the demo address form",
"fields": [
{
"type": "string",
"name": "form_title",
"description": "The heading or title of the form"
},
{
"type": "object",
"name": "full_name",
"description": "Full name input field",
"fields": [
{
"type": "string",
"name": "label",
"description": "The visible label text"
},
{
"type": "string",
"name": "placeholder",
"description": "Placeholder text shown in the input"
},
{
"type": "string",
"name": "input_name",
"description": "The name attribute of the input element"
}
]
},
{
"type": "object",
"name": "address_line_1",
"description": "First address line input field",
"fields": [
{
"type": "string",
"name": "label",
"description": "The visible label text"
},
{
"type": "string",
"name": "placeholder",
"description": "Placeholder text shown in the input"
},
{
"type": "string",
"name": "input_name",
"description": "The name attribute of the input element"
}
]
},
{
"type": "object",
"name": "address_line_2",
"description": "Second address line input field",
"fields": [
{
"type": "string",
"name": "label",
"description": "The visible label text"
},
{
"type": "string",
"name": "placeholder",
"description": "Placeholder text shown in the input"
},
{
"type": "string",
"name": "input_name",
"description": "The name attribute of the input element"
}
]
},
{
"type": "object",
"name": "city",
"description": "City input field",
"fields": [
{
"type": "string",
"name": "label",
"description": "The visible label text"
},
{
"type": "string",
"name": "placeholder",
"description": "Placeholder text shown in the input"
},
{
"type": "string",
"name": "input_name",
"description": "The name attribute of the input element"
}
]
},
{
"type": "object",
"name": "postcode",
"description": "Postcode or ZIP code input field",
"fields": [
{
"type": "string",
"name": "label",
"description": "The visible label text"
},
{
"type": "string",
"name": "placeholder",
"description": "Placeholder text shown in the input"
},
{
"type": "string",
"name": "input_name",
"description": "The name attribute of the input element"
}
]
},
{
"type": "object",
"name": "country",
"description": "Country selection dropdown",
"fields": [
{
"type": "string",
"name": "label",
"description": "The visible label text"
},
{
"type": "string",
"name": "input_name",
"description": "The name attribute of the select element"
},
{
"type": "array",
"name": "options",
"description": "Available country options in the dropdown",
"fields": [
{
"type": "string",
"name": "value",
"description": "The option value or text"
}
]
}
]
}
]
},
"instruction": "Extract all visible form fields from this address form, including their labels, input names, placeholders, and for dropdown fields, list all available options.",
"model": "gpt-4o-mini",
"output_type": "inline"
}
]
}
}{
"data": {
"id": "brq_VYg5H56A7m4vLJTdzj2jB3MgTAfT7K",
"url": "https://demo.gaffa.dev/simulate/form?loadTime=3&showModal=true&modalDelay=5&formType=address",
"state": "completed",
"credit_usage": 0,
"http_status_code": 200,
"from_cache": false,
"started_at": "2025-12-01T06:40:15.9241312Z",
"completed_at": "2025-12-01T06:40:23.7495525Z",
"running_time": "00:00:07.8254213",
"page_load_time": "00:00:00.3124478",
"actions": [
{
"id": "act_VYg5HDUFBrWq1GdmhQruRq4Gp7hjAk",
"type": "parse_json",
"timestamp": "2025-12-01T06:40:23.7495396Z",
"output": {
"form_title": "Address Form",
"full_name": {
"label": "Full Name",
"placeholder": "Enter your full name",
"input_name": "full_name"
},
"address_line_1": {
"label": "Address Line 1",
"placeholder": "Enter your address",
"input_name": "address_line_1"
},
"address_line_2": {
"label": "Address Line 2",
"placeholder": "Optional",
"input_name": "address_line_2"
},
"city": {
"label": "City",
"placeholder": "Enter your city",
"input_name": "city"
},
"postcode": {
"label": "Postcode",
"placeholder": "Enter your postcode",
"input_name": "postcode"
},
"country": {
"label": "Country",
"input_name": "country",
"options": [
{
"value": "United States"
},
{
"value": "Canada"
},
{
"value": "United Kingdom"
},
{
"value": "Australia"
},
{
"value": "Germany"
}
]
}
},
"reference": "https://storage.gaffa.dev/brq/dom/brq_VYg5H56A7m4vLJTdzj2jB3MgTAfT7K/act_VYg5HDUFBrWq1GdmhQruRq4Gp7hjAk_raw.txt"
}
]
}
}def main():
site_url = "https://gaffa.dev"
sitemap_urls = get_sitemap_urls(site_url)[:3]
for i, url in enumerate(sitemap_urls, 1):
dom_content = get_dom(url)
image_urls = extract_image_urls(dom_content, url)
if image_urls:
download_image(image_urls[0], f"image_{i}")
if __name__ == "__main__":
main()python3 gaffa_scrape_images.py# Create a new directory and navigate into it
mkdir gaffa-image-scraper && cd gaffa-image-scraper
# Create a virtual environment (optional but recommended)
python -m venv venv
source venv/bin/activate# On macOS/Linux
export GAFFA_API_KEY='your_gaffa_api_key_here'def get_sitemap_urls(site_url, max_cache_age=86400):
payload = {
"url": site_url,
"max_cache_age": max_cache_age
}
print("Retrieving sitemap URLs.")
response = requests.post("https://api.gaffa.dev/v1/site/map",
json=payload, headers=HEADERS)
return response.json()["data"]["links"]def get_dom(url):
payload = {
"url": url,
"async": False,
"settings": {
"actions": [
{"type": "wait", "selector": "img", "timeout": 20000},
{"type": "capture_dom"}
],
"time_limit": 40000
}
}
print("Capturing DOM URL.")
response = requests.post("https://api.gaffa.dev/v1/browser/requests",
json=payload, headers=HEADERS)
dom_url = response.json()["data"]["actions"][1]["output"]
print("Retrieving DOM.")
dom_response = requests.get(dom_url)
return dom_response.textdef extract_image_urls(dom_content, base_url):
image_urls = []
src_pattern = r'<img[^>]+(?:src|data-src)=["\']([^"\']+)["\']'
matches = re.findall(src_pattern, dom_content)
for src in matches:
if not src.startswith(('http:', 'https:')):
src = urljoin(base_url, src)
image_urls.append(src)
return image_urls
def download_image(image_url, filename):
payload = {
"url": image_url,
"async": False,
"settings": {
"actions": [{"type": "download_file"}]
}
}
print("Retrieving download URL.")
response = requests.post("https://api.gaffa.dev/v1/browser/requests", json=payload, headers=HEADERS)
actions = response.json()["data"]["actions"]
download_url = actions[0]["output"]
download_ext = os.path.splitext(download_url)[1]
print("Downloading image.")
img_response = requests.get(download_url)
filepath = f"{filename}{download_ext}"
with open(filepath, 'wb') as f:
f.write(img_response.content)
python -m venv venv && source venv/bin/activatepip install requests openaiGAFFA_API_KEY=your_gaffa_api_keypip install requestsmkdir gaffa-form-filler
cd gaffa-form-fillerimport requests
import json
# Configuration
GAFFA_API_KEY = "your_api_key_here" # Replace with your actual API key
GAFFA_API_URL = "https://api.gaffa.dev/v1/request"
# The demo form we'll work with
FORM_URL = "https://demo.gaffa.dev/simulate/form?loadTime=3&showModal=true&modalDelay=5&formType=address"def extract_form_fields(form_url):
payload = {
"url": form_url,
"async": False,
"settings": {
"record_request": False,
"actions": [
{
"type": "wait",
"selector": "form",
"timeout": 10000
},
{
"type": "parse_json",
"data_schema": {
"name": "FormFields",
"description": "Extract all form input fields",
"fields": [
{"type": "string", "name": "form_title", "description": "Form title"},
{
"type": "array",
"name": "fields",
"description": "List of all input fields",
"fields": [
{"type": "string", "name": "label", "description": "Field label"},
{"type": "string", "name": "field_name", "description": "Field name attribute"},
{"type": "string", "name": "field_type", "description": "Input type"},
{"type": "boolean", "name": "required", "description": "Is required?"},
{"type": "string", "name": "placeholder", "description": "Placeholder text"}
]
}
]
},
"instruction": "Extract all form fields with their properties",
"model": "gpt-4o-mini",
"output_type": "inline"
}
]
}
}
headers = {"X-API-Key": GAFFA_API_KEY, "Content-Type": "application/json"}
response = requests.post(GAFFA_API_URL, json=payload, headers=headers)
response.raise_for_status()
result = response.json()
for action in result["data"]["actions"]:
if action.get("type") == "parse_json":
return action["output"]
return Nonedef collect_user_input(form_data):
print(f"\n{'='*60}")
print(f"📋 Form: {form_data.get('form_title', 'Unknown Form')}")
print(f"{'='*60}\n")
user_values = {}
fields = form_data.get("fields", [])
if not fields:
print("⚠️ No fields found in the form")
return user_values
print(f"Please provide values for {len(fields)} field(s):\n")
for i, field in enumerate(fields, 1):
label = field.get("label", "Unknown Field")
field_name = field.get("field_name", "")
required = field.get("required", False)
placeholder = field.get("placeholder", "")
required_marker = " *" if required else ""
placeholder_hint = f" (e.g., {placeholder})" if placeholder else ""
prompt = f"[{i}/{len(fields)}] {label}{required_marker}{placeholder_hint}: "
while True:
value = input(prompt).strip()
if required and not value:
print(" ⚠️ This field is required. Please provide a value.")
continue
if not value and not required:
print(" ℹ️ Skipping optional field")
break
user_values[field_name] = value
break
return user_valuesdef fill_form(form_url, field_values):
if not field_values:
return None
actions = [
{
"type": "wait",
"selector": "form",
"timeout": 10000
}
]
for field_name, value in field_values.items():
if value:
actions.append({
"type": "type",
"selector": f"[name='{field_name}']",
"text": value
})
actions.extend([
{"type": "click", "selector": "button[type='submit']"},
{"type": "capture_screenshot", "size": "fullscreen"}
])
payload = {
"url": form_url,
"async": False,
"settings": {
"record_request": False,
"actions": actions
}
}
headers = {"X-API-Key": GAFFA_API_KEY, "Content-Type": "application/json"}
response = requests.post(GAFFA_API_URL, json=payload, headers=headers)
response.raise_for_status()
return response.json()def main():
print("\n" + "="*60)
print("🤖 Gaffa Form Filler")
print("="*60)
print("This tool extracts form fields and helps you fill them out.\n")
print("📋 Step 1: Analyzing form...")
form_data = extract_form_fields(FORM_URL)
if not form_data:
print("\n❌ Could not extract form fields")
return
print(f"✅ Found {len(form_data.get('fields', []))} field(s)\n")
print("📝 Step 2: Collecting your input...")
user_values = collect_user_input(form_data)
if not user_values:
print("\n⚠️ No values provided. Exiting.")
return
print(f"\n{'='*60}")
print("📊 Summary of values to submit:")
print(f"{'='*60}")
for field_name, value in user_values.items():
print(f" {field_name}: {value}")
print(f"{'='*60}\n")
confirm = input("Submit this form? (y/n): ").strip().lower()
if confirm != 'y':
print("\n❌ Submission cancelled")
return
print("\n🚀 Step 3: Submitting form...")
result = fill_form(FORM_URL, user_values)
if not result:
print("❌ Form submission failed")
return
print("\n✅ Form submitted successfully!")
if "data" in result and "actions" in result["data"]:
for action in result["data"]["actions"]:
if action.get("type") == "capture_screenshot" and "output" in action:
print(f"📸 Screenshot: {action['output']}")
print("\n🎉 All done!\n")
if __name__ == "__main__":
main()python your_script_name.py============================================================
🤖 Gaffa Form Filler
============================================================
This tool extracts form fields and helps you fill them out.
📋 Step 1: Analyzing form...
✅ Found 9 field(s)
📝 Step 2: Collecting your input...
============================================================
📋 Form: Form Submission Test
============================================================
Please provide values for 9 field(s):
[1/9] First Name *: John
[2/9] Last Name *: Smith
[3/9] Email *: john@example.com
...
============================================================
📊 Summary of values to submit:
============================================================
first_name: John
last_name: Smith
email: john@example.com
...
Submit this form? (y/n): y
🚀 Step 3: Submitting form...
✅ Form submitted successfully!
🎉 All done!fields"actions": [
{
"type": "parse_json",
"data_schema": {
"name": "ArticleMetadata",
"instruction": "Extract metadata from an article",
"fields": [
{
"type": "string",
"name": "title",
"description": "Article title"
},
{
"type": "string",
"name": "author",
"description": "Author name"
},
{
"type": "datetime",
"name": "published",
"description": "Publication date"
}
]
},
"model": "gpt-4o-mini",
"output_type": "inline"
}
]curl -L \
--request POST \
--url 'https://api.gaffa.dev/v1/schemas' \
--header 'X-API-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"name": "ProductInfo",
"instruction": "Extract product details from e-commerce pages",
"fields": [
{
"type": "string",
"name": "product_name",
"description": "The product title"
},
{
"type": "decimal",
"name": "price",
"description": "Current price"
},
{
"type": "boolean",
"name": "in_stock",
"description": "Product availability"
},
{
"type": "object",
"name": "ratings",
"description": "Product rating information",
"fields": [
{
"type": "double",
"name": "average",
"description": "Average rating score"
},
{
"type": "integer",
"name": "total_reviews",
"description": "Number of reviews"
}
]
},
{
"type": "array",
"name": "tags",
"description": "Product tags",
"fields": [
{
"type": "string",
"name": "tag",
"description": "Individual tag name"
}
]
}
]
}'{
"id": "schema_abc123xyz",
"name": "ProductInfo",
"description": "Extract product details from e-commerce pages",
"fields": [...]
}curl -L \
--url 'https://api.gaffa.dev/v1/schemas' \
--header 'X-API-Key: YOUR_API_KEY' \
--header 'Accept: */*'curl -L \
--request PUT \
--url 'https://api.gaffa.dev/v1/schemas/{id}' \
--header 'X-API-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"id": "schema_abc123xyz",
"name": "ProductInfo",
"instruction": "Extract detailed product information from e-commerce pages",
"fields": [
{
"type": "string",
"name": "product_name",
"description": "The product title"
},
{
"type": "decimal",
"name": "price",
"description": "Current price"
},
{
"type": "string",
"name": "brand",
"description": "Product brand name"
}
]
}'curl -L \
--request DELETE \
--url 'https://api.gaffa.dev/v1/schemas/{id}' \
--header 'X-API-Key: YOUR_API_KEY' \
--header 'Accept: */*'{
"name": "TagList",
"instruction": "Extract article tags",
"fields": [
{
"type": "array",
"name": "tags",
"description": "List of article tags",
"fields": [
{
"type": "string",
"name": "tag",
"description": "Individual tag name"
}
]
}
]
}{
"name": "ProductWithReviews",
"instruction": "Product details with nested review data",
"fields": [
{
"type": "string",
"name": "product_name",
"description": "Product name"
},
{
"type": "object",
"name": "pricing",
"description": "Pricing information",
"fields": [
{
"type": "decimal",
"name": "current_price",
"description": "Current price"
},
{
"type": "decimal",
"name": "original_price",
"description": "Original price before discount"
},
{
"type": "integer",
"name": "discount_percentage",
"description": "Discount percentage"
}
]
}
]
}The unique identifiers of the browser requests to retrieve.
{"value":"brq_V2P6PqrZpycFtbc7mtXE4tsNbeg2N6,brq_V2P6X38RDRMRyYcNJ82qPSH5eFfQRD"}The statuses of the browser requests to filter by. Valid values: pending, running, completed, failed
{"value":"completed,running"}Items to return per page (default: 30).
{"value":20}Page number of the pagination (default: 1).
{"value":1}A collection of browser requests that match the criteria
The total number of pages available
The total number of records across all pages
ID of the browser request
URL of the request
The proxy location of the request.
The status of the request
The number of credits used by the request
The name of the error type
More detail about the error
The actual URL captured, after any redirects.
The http status code for the request.
If this request was served from the cached
The time in UTC when the request started.
The time in UTC when the request finished.
The running time of the request
How long did the page take to fully render.
ID of the action
Name of the action
Custom ID of the action
Time the action was initiated
Ouput of the action, if any
Reference file for the action, if any
Number of iterations completed for loop actions
ID of the action
Name of the action
Custom ID of the action
Time the action was initiated
Ouput of the action, if any
Reference file for the action, if any
Number of iterations completed for loop actions
Error message, if any
Error message, if any
Video url
The page number to return (1-based)
1The number of records to return per page
30Invalid query parameters
The type of object this is concerning
The id of the item concerned.
Error code.
Error description.
The url you want our sitemap reader to process on your behalf
Maximum cache age in seconds for this request. If a cached result exists within this timeframe, it will be returned. Default is 0 (no cache).
The sitemap request response detailing the URLs found
ID of the sitemap request
URL of the request
The status of the request
The number of credits used by the request
The name of the error type
More detail about the error
If this request was served from the cache
The time in UTC when the request started.
The time in UTC when the request finished.
The running time of the request
List of URLs found in the sitemap
Number of links found
The sitemap request timed out after 60 seconds
The requested site is unavailable
The location of the proxy server that your request will be routed through, null means no proxy is used
nullThe url you want our browsers to visit on your behalf
Whether the request should be processed asynchronously, synchronous requests can be maximum 60 seconds long.
trueThe maximum age of a cached result in seconds. 0 means the cache will never be used
0Record a video of this request
falseCap the maximum time the request should take to complete, in milliseconds (default: 60000)
60000Cap the maximum bandwidth to use for media downloads, in MB
Enable ad blocking for this request
falseThe browser request response detailing the state and output of the request
ID of the browser request
URL of the request
The proxy location of the request.
The status of the request
The number of credits used by the request
The name of the error type
More detail about the error
The actual URL captured, after any redirects.
The http status code for the request.
If this request was served from the cached
The time in UTC when the request started.
The time in UTC when the request finished.
The running time of the request
How long did the page take to fully render.
ID of the action
Name of the action
Custom ID of the action
Time the action was initiated
Ouput of the action, if any
Reference file for the action, if any
Number of iterations completed for loop actions
ID of the action
Name of the action
Custom ID of the action
Time the action was initiated
Ouput of the action, if any
Reference file for the action, if any
Number of iterations completed for loop actions
Error message, if any
Error message, if any
Video url
The browser request timed out - an example error
The unique identifiers of the sitemap requests to retrieve.
{"value":"smr_1234567890abcdef,smr_0987654321fedcba"}The statuses of the sitemap requests to filter by. Valid values: pending, completed, failed
{"value":"completed,pending"}Items to return per page (default: 30).
{"value":30}Page number of the pagination (default: 1).
{"value":1}A collection of sitemap requests that match the criteria
The total number of pages available
The total number of records across all pages
ID of the sitemap request
URL of the request
The status of the request
The number of credits used by the request
The name of the error type
More detail about the error
If this request was served from the cache
The time in UTC when the request started.
The time in UTC when the request finished.
The running time of the request
List of URLs found in the sitemap
Number of links found
The page number to return (1-based)
1The number of records to return per page
30Invalid query parameters
The type of object this is concerning
The id of the item concerned.
Error code.
Error description.
The unique identifier for the data schema.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
Payload of DataSchema
The unique identifier for the data schema.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
Payload of PagedResult containing DataSchema
The total number of pages available
The total number of records across all pages
The unique identifier for the data schema.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
The page number to return (1-based)
1The number of records to return per page
30Payload of PagedResult containing DataSchema
The unique identifier of the sitemap request to retrieve.
The sitemap request
ID of the sitemap request
URL of the request
The status of the request
The number of credits used by the request
The name of the error type
More detail about the error
If this request was served from the cache
The time in UTC when the request started.
The time in UTC when the request finished.
The running time of the request
List of URLs found in the sitemap
Number of links found
Sitemap request not found
The unique identifier for the data schema.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
Payload of DataSchema
The unique identifier for the data schema.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
The type of the field.
The name of the schema or field.
A description of the schema or field.
The unique identifier of the browser request to retrieve.
The unique identifiers of the browser request to retrieve.
The browser request
ID of the browser request
URL of the request
The proxy location of the request.
The status of the request
The number of credits used by the request
The name of the error type
More detail about the error
The actual URL captured, after any redirects.
The http status code for the request.
If this request was served from the cached
The time in UTC when the request started.
The time in UTC when the request finished.
The running time of the request
How long did the page take to fully render.
ID of the action
Name of the action
Custom ID of the action
Time the action was initiated
Ouput of the action, if any
Reference file for the action, if any
Number of iterations completed for loop actions
ID of the action
Name of the action
Custom ID of the action
Time the action was initiated
Ouput of the action, if any
Reference file for the action, if any
Number of iterations completed for loop actions
Error message, if any
Error message, if any
Video url
Browser request not found
Payload of DataSchema
Payload of DataSchema
{"data":{"total_pages":1,"total_records":2,"results":[{"id":"brq_V2PUfFA8AQPAQ5VEsewpxdGUSZkgKP","url":"https://demo.gaffa.dev/simulate/table?loadTime=3&rowCount=20","proxy_location":null,"state":"completed","credit_usage":4,"error":null,"error_reason":null,"actual_url":null,"http_status_code":200,"from_cache":false,"started_at":"2024-11-22T16:31:13.128103+00:00","completed_at":"2024-11-22T16:31:47.020851+00:00","running_time":null,"page_load_time":"00:00:03.4705813","actions":[{"id":"act_V2PUfETiTdXwzEgAW2NPURnATW7we9","type":"wait","custom_id":null,"timestamp":"2024-11-22T16:31:16.6080484+00:00","output":null,"reference":null,"iterations":null,"actions":null,"error":null},{"id":"act_V2PUfBreQxHR2SNqGXuPzzWoiyRsrm","type":"print","custom_id":null,"timestamp":"2024-11-22T16:31:40.5760333+00:00","output":"https://storage.gaffa.dev/brq/pdf/brq_V2PUfFA8AQPAQ5VEsewpxdGUSZkgKP/act_V2PUfBreQxHR2SNqGXuPzzWoiyRsrm.pdf","reference":null,"iterations":null,"actions":null,"error":null}],"video":"https://storage.gaffa.dev/brq/video/brq_V2PUfFA8AQPAQ5VEsewpxdGUSZkgKP.mp4"},{"id":"brq_V2NmHY9FsvPQEGbfVBSeV6UCp2SXjC","url":"https://demo.gaffa.dev/simulate/article?loadTime=3¶graphs=10&images=3","proxy_location":null,"state":"completed","credit_usage":1,"error":null,"error_reason":null,"actual_url":null,"http_status_code":200,"from_cache":false,"started_at":"2024-11-22T12:52:48.708264+00:00","completed_at":"2024-11-22T12:52:54.25994+00:00","running_time":null,"page_load_time":"00:00:00.8094888","actions":[{"id":"act_V2NmHijnQa9iPDNcvhjS2GGFt5se8j","type":"wait","custom_id":null,"timestamp":"2024-11-22T12:52:49.5690537+00:00","output":null,"reference":null,"iterations":null,"actions":null,"error":null},{"id":"act_V2NmHgs27VJKB49YavtK4CcyErdfvD","type":"generate_markdown","custom_id":null,"timestamp":"2024-11-22T12:52:52.8353136+00:00","output":"https://storage.gaffa.dev/brq/md/brq_V2NmHY9FsvPQEGbfVBSeV6UCp2SXjC/act_V2NmHgs27VJKB49YavtK4CcyErdfvD.md","reference":null,"iterations":null,"actions":null,"error":null}],"video":null},{"id":"brq_V2HvS2cw4Z2wonqEAwbxoxjrkmRdEM","url":"https://demo.gaffa.dev/simulate/article","proxy_location":null,"state":"failed","credit_usage":0,"error":null,"error_reason":null,"actual_url":null,"http_status_code":null,"from_cache":false,"started_at":null,"completed_at":null,"running_time":null,"page_load_time":null,"actions":null,"video":null}],"page":1,"page_size":30},"error":null}GET /v1/browser/requests HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Accept: */*
DELETE /v1/schemas/{id} HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Accept: */*
{"id":"smr_1234567890abcdef","url":"https://example.com","state":"completed","credit_usage":1,"error":null,"error_reason":null,"from_cache":false,"started_at":"2024-01-01T12:00:00+00:00","completed_at":"2024-01-01T12:00:30+00:00","running_time":"00:00:30","links":["https://example.com/","https://example.com/about","https://example.com/products","https://example.com/contact"],"link_count":4}POST /v1/site/map HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 55
"{\"url\":\"https://example.com\",\"max_cache_age\":0}"{"data":{"id":"brq_V2P6PqrZpycFtbc7mtXE4tsNbeg2N6","url":"https://demo.gaffa.dev/simulate/table?loadTime=3&rowCount=20","proxy_location":null,"state":"completed","credit_usage":1,"error":null,"error_reason":null,"actual_url":null,"http_status_code":200,"from_cache":false,"started_at":"2024-11-22T14:33:38.7762685+00:00","completed_at":"2024-11-22T14:33:42.7135779+00:00","running_time":null,"page_load_time":"00:00:00.1902889","actions":[{"id":"act_V2P6Q3fSHhBWAhf4BQJxj9oYbQF1V9","type":"wait","custom_id":null,"timestamp":"2024-11-22T14:33:38.9665719+00:00","output":null,"reference":null,"iterations":null,"actions":null,"error":null},{"id":"act_V2P6Q6BuQhoYDAVkQMVSKkkwmUrArb","type":"print","custom_id":null,"timestamp":"2024-11-22T14:33:42.3025888+00:00","output":"https://storage.gaffa.dev/brq/pdf/brq_V2P6PqrZpycFtbc7mtXE4tsNbeg2N6/....","reference":null,"iterations":null,"actions":null,"error":null}],"video":null},"error":null}POST /v1/browser/requests HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 370
"{\"proxy_location\":null,\"url\":\"https://demo.gaffa.dev/simulate/table?loadTime=3&rowCount=20\",\"async\":false,\"max_cache_age\":0,\"settings\":{\"record_request\":false,\"actions\":[{\"type\":\"wait\",\"selector\":\"table\"},{\"type\":\"print\",\"size\":\"A4\",\"margin\":20}],\"time_limit\":60000,\"max_media_bandwidth\":null,\"output\":null,\"block_ads\":false}}"{"total_pages":0,"total_records":1,"results":[{"id":"smr_1234567890abcdef","url":"https://example.com","state":"completed","credit_usage":1,"error":null,"error_reason":null,"from_cache":false,"started_at":"2024-01-01T12:00:00+00:00","completed_at":"2024-01-01T12:01:00+00:00","running_time":"00:01:00","links":["https://example.com/","https://example.com/about","https://example.com/products"],"link_count":3}],"page":1,"page_size":30}GET /v1/site/map HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Accept: */*
{
"id": "text",
"name": "text",
"description": "text",
"fields": [
{
"type": 0,
"name": "text",
"description": "text",
"fields": [
{
"type": 0,
"name": "text",
"description": "text",
"fields": [
"[Circular Reference]"
]
}
]
}
]
}PUT /v1/schemas/{id} HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 1082
"{\"name\":\"Updated Product Schema\",\"description\":\"Enhanced schema for product information with additional fields\",\"fields\":[{\"type\":\"string\",\"name\":\"productName\",\"description\":\"Name of the product\",\"fields\":[]},{\"type\":\"decimal\",\"name\":\"price\",\"description\":\"Product price\",\"fields\":[]},{\"type\":\"boolean\",\"name\":\"inStock\",\"description\":\"Whether the product is in stock\",\"fields\":[]},{\"type\":\"array\",\"name\":\"tags\",\"description\":\"Product tags\",\"fields\":[{\"type\":\"string\",\"name\":\"tagItem\",\"description\":null,\"fields\":[]}]},{\"type\":\"array\",\"name\":\"categories\",\"description\":\"Product categories\",\"fields\":[{\"type\":\"string\",\"name\":\"category\",\"description\":null,\"fields\":[]}]},{\"type\":\"object\",\"name\":\"specifications\",\"description\":\"Technical specifications\",\"fields\":[{\"type\":\"string\",\"name\":\"dimensions\",\"description\":\"Product dimensions\",\"fields\":[]},{\"type\":\"double\",\"name\":\"weight\",\"description\":\"Product weight in grams\",\"fields\":[]}]}]}"GET /v1/schemas HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Accept: */*
{"data":{"total_pages":1,"total_records":3,"results":[{"id":"schema_abc123def456","name":"Customer Schema","description":"Data schema for customer information","fields":[{"type":"string","name":"firstName","description":"Customer's first name","fields":[]},{"type":"string","name":"lastName","description":"Customer's last name","fields":[]},{"type":"integer","name":"age","description":"Customer's age in years","fields":[]},{"type":"boolean","name":"isActive","description":"Whether the customer account is active","fields":[]}]},{"id":"schema_xyz789uvw123","name":"Product Schema","description":"Data schema for product information","fields":[{"type":"string","name":"productName","description":"Name of the product","fields":[]},{"type":"decimal","name":"price","description":"Product price","fields":[]},{"type":"boolean","name":"inStock","description":"Whether the product is in stock","fields":[]},{"type":"array","name":"tags","description":"Product tags","fields":[{"type":"string","name":"tagItem","description":null,"fields":[]}]}]},{"id":"schema_hij456klm789","name":"Order Schema","description":"Data schema for order processing","fields":[{"type":"string","name":"orderId","description":"Unique order identifier","fields":[]},{"type":"datetime","name":"orderDate","description":"Date when order was placed","fields":[]},{"type":"object","name":"customer","description":"Customer information","fields":[{"type":"string","name":"customerId","description":"Customer identifier","fields":[]},{"type":"string","name":"email","description":"Customer email address","fields":[]}]},{"type":"decimal","name":"totalAmount","description":"Total order amount","fields":[]}]}],"page":1,"page_size":30},"error":null}GET /v1/site/map/{id} HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Accept: */*
{
"id": "text",
"url": "text",
"state": "text",
"credit_usage": 1,
"error": "text",
"error_reason": "text",
"from_cache": true,
"started_at": "2026-06-25T02:09:35.290Z",
"completed_at": "2026-06-25T02:09:35.290Z",
"running_time": "text",
"links": [
"text"
],
"link_count": 1
}{"id":"schema_abc123def456","name":"Customer Schema","description":"Data schema for customer information","fields":[{"type":"string","name":"firstName","description":"Customer's first name","fields":[]},{"type":"string","name":"lastName","description":"Customer's last name","fields":[]},{"type":"integer","name":"age","description":"Customer's age in years","fields":[]},{"type":"boolean","name":"isActive","description":"Whether the customer account is active","fields":[]}]}POST /v1/schemas HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 518
"{\"name\":\"Customer Schema\",\"description\":\"Data schema for customer information\",\"fields\":[{\"type\":\"string\",\"name\":\"firstName\",\"description\":\"Customer's first name\",\"fields\":[]},{\"type\":\"string\",\"name\":\"lastName\",\"description\":\"Customer's last name\",\"fields\":[]},{\"type\":\"integer\",\"name\":\"age\",\"description\":\"Customer's age in years\",\"fields\":[]},{\"type\":\"boolean\",\"name\":\"isActive\",\"description\":\"Whether the customer account is active\",\"fields\":[]}]}"GET /v1/browser/requests/{id}?id=text HTTP/1.1
Host: api.gaffa.dev
X-API-Key: YOUR_API_KEY
Accept: */*
{
"id": "text",
"url": "text",
"proxy_location": "text",
"state": "text",
"credit_usage": 1,
"error": "text",
"error_reason": "text",
"actual_url": "text",
"http_status_code": 1,
"from_cache": true,
"started_at": "2026-06-25T02:09:35.290Z",
"completed_at": "2026-06-25T02:09:35.290Z",
"running_time": "text",
"page_load_time": "text",
"actions": [
{
"id": "text",
"type": "text",
"custom_id": "text",
"timestamp": "2026-06-25T02:09:35.290Z",
"output": {},
"reference": "text",
"iterations": 1,
"actions": [
{
"id": "text",
"type": "text",
"custom_id": "text",
"timestamp": "2026-06-25T02:09:35.290Z",
"output": {},
"reference": "text",
"iterations": 1,
"actions": [
"[Circular Reference]"
],
"error": "text"
}
],
"error": "text"
}
],
"video": "text"
}