> ## Documentation Index
> Fetch the complete documentation index at: https://webscraping.titannet.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# API call

> Use the api_call action type to invoke external HTTP APIs as part of a Titan task workflow.

The **`api_call`** action type is for **HTTP tool steps**: call partner APIs, internal microservices, or other structured endpoints with payloads and validation defined in your task (and template or script). It is not “scraping HTML”; it is **first-class orchestration** alongside **`search`**, **`crawl`**, and **`scrape`**.

<Note>
  Examples use **`TITAN_API_URL`** and **`TITAN_TOKEN`**. Tab titles match other integration pages. **Rust**: **ureq** + **serde\_json**.
</Note>

## When to use `api_call`

* A vendor exposes **REST or GraphQL** you must call before or after web steps.
* You need **stable JSON** from an API plus **on-page signals** only available via **`scrape`**.
* You want one Titan task to represent **API + web** work with shared execution history and billing.

The service applies **extra validation** for certain **`api_call`** URL and payload combinations—expect clear **`400`** errors from **`POST /api/v1/tasks`** when configuration is inconsistent.

## Single-action example (`POST /api/v1/tasks`)

**Secrets** (API keys, OAuth client secrets) must not appear in **`payload`** on create if your policy forbids it—use **`secret_payload` on `POST /api/v1/tasks/:id/run`** instead, or inject via your vault integration at run time.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -sS -X POST "$TITAN_API_URL/api/v1/tasks" \
      -H "Authorization: Bearer $TITAN_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Shipping quotes API pull",
        "objective": "POST to the carrier quote API and normalize the JSON response",
        "execution_type": "single",
        "urls": ["https://api.carrier.example/v1/quotes"],
        "action_type": "api_call",
        "input_source": "static_urls",
        "template_slug": "carrier-quote-json",
        "payload": {
          "origin_zip": "94107",
          "destination_zip": "10001",
          "weight_kg": 12.5
        },
        "limits": {
          "timeout_seconds": 20,
          "max_retries": 2
        }
      }'
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    body := map[string]any{
      "name":         "Shipping quotes API pull",
      "objective":    "POST to the carrier quote API and normalize the JSON response",
      "execution_type": "single",
      "urls":         []string{"https://api.carrier.example/v1/quotes"},
      "action_type":  "api_call",
      "input_source": "static_urls",
      "template_slug": "carrier-quote-json",
      "payload": map[string]any{
        "origin_zip": "94107", "destination_zip": "10001", "weight_kg": 12.5,
      },
      "limits": map[string]any{"timeout_seconds": 20, "max_retries": 2},
    }
    b, _ := json.Marshal(body)
    req, _ := http.NewRequest("POST", os.Getenv("TITAN_API_URL")+"/api/v1/tasks", bytes.NewReader(b))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
    req.Header.Set("Content-Type", "application/json")
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    out, _ := io.ReadAll(resp.Body)
    fmt.Println(string(out))
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const base = process.env.TITAN_API_URL!;
    const token = process.env.TITAN_TOKEN!;
    const res = await fetch(`${base}/api/v1/tasks`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "Shipping quotes API pull",
        objective: "POST to the carrier quote API and normalize the JSON response",
        execution_type: "single",
        urls: ["https://api.carrier.example/v1/quotes"],
        action_type: "api_call",
        input_source: "static_urls",
        template_slug: "carrier-quote-json",
        payload: {
          origin_zip: "94107",
          destination_zip: "10001",
          weight_kg: 12.5,
        },
        limits: { timeout_seconds: 20, max_retries: 2 },
      }),
    });
    console.log(await res.text());
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import json
    import os
    import urllib.request

    payload = {
        "name": "Shipping quotes API pull",
        "objective": "POST to the carrier quote API and normalize the JSON response",
        "execution_type": "single",
        "urls": ["https://api.carrier.example/v1/quotes"],
        "action_type": "api_call",
        "input_source": "static_urls",
        "template_slug": "carrier-quote-json",
        "payload": {
            "origin_zip": "94107",
            "destination_zip": "10001",
            "weight_kg": 12.5,
        },
        "limits": {"timeout_seconds": 20, "max_retries": 2},
    }
    req = urllib.request.Request(
        f"{os.environ['TITAN_API_URL']}/api/v1/tasks",
        data=json.dumps(payload).encode(),
        headers={
            "Authorization": f"Bearer {os.environ['TITAN_TOKEN']}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    with urllib.request.urlopen(req) as resp:
        print(resp.read().decode())
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    use serde_json::json;

    let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
    let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
    let body = json!({
        "name": "Shipping quotes API pull",
        "objective": "POST to the carrier quote API and normalize the JSON response",
        "execution_type": "single",
        "urls": ["https://api.carrier.example/v1/quotes"],
        "action_type": "api_call",
        "input_source": "static_urls",
        "template_slug": "carrier-quote-json",
        "payload": {
            "origin_zip": "94107",
            "destination_zip": "10001",
            "weight_kg": 12.5
        },
        "limits": { "timeout_seconds": 20, "max_retries": 2 }
    });
    let resp = ureq::post(format!("{base}/api/v1/tasks"))
        .set("Authorization", &format!("Bearer {token}"))
        .set("Content-Type", "application/json")
        .send_json(body)
        .expect("create");
    println!("{}", resp.into_string().expect("body"));
    ```
  </Tab>
</Tabs>

## Chained example (api\_call → scrape)

Use an **`execution_plan`** when the API response contains URLs you must scrape next:

```json theme={null}
{
  "name": "Resolve listings via API then scrape PDPs",
  "objective": "Fetch listing URLs from our marketplace API, then scrape each PDP",
  "execution_type": "single",
  "execution_plan": {
    "steps": [
      {
        "step_id": "listings",
        "action_type": "api_call",
        "input_source": "static_urls",
        "template_slug": "marketplace-listing-api",
        "limits": { "timeout_seconds": 15 }
      },
      {
        "step_id": "pdps",
        "action_type": "scrape",
        "input_source": "previous_step",
        "template_slug": "commerce-pdp",
        "output_schema": {
          "type": "object",
          "properties": {
            "price": { "type": "number" }
          }
        }
      }
    ]
  }
}
```

## Trigger via executions API

Replace the UUID with a real **`task_id`**. Modular tasks load the **stored workflow**; you generally do **not** override `execution_plan` on this call.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -sS -X POST "$TITAN_API_URL/api/v1/executions" \
      -H "Authorization: Bearer $TITAN_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "task_id": "00000000-0000-0000-0000-000000000000",
        "payload": { "refresh": true }
      }'
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    body := map[string]any{
      "task_id": "00000000-0000-0000-0000-000000000000",
      "payload": map[string]bool{"refresh": true},
    }
    b, _ := json.Marshal(body)
    req, _ := http.NewRequest("POST", os.Getenv("TITAN_API_URL")+"/api/v1/executions", bytes.NewReader(b))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
    req.Header.Set("Content-Type", "application/json")
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    out, _ := io.ReadAll(resp.Body)
    fmt.Println(string(out))
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const base = process.env.TITAN_API_URL!;
    const token = process.env.TITAN_TOKEN!;
    const res = await fetch(`${base}/api/v1/executions`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        task_id: "00000000-0000-0000-0000-000000000000",
        payload: { refresh: true },
      }),
    });
    console.log(await res.text());
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import json
    import os
    import urllib.request

    payload = {
        "task_id": "00000000-0000-0000-0000-000000000000",
        "payload": {"refresh": True},
    }
    req = urllib.request.Request(
        f"{os.environ['TITAN_API_URL']}/api/v1/executions",
        data=json.dumps(payload).encode(),
        headers={
            "Authorization": f"Bearer {os.environ['TITAN_TOKEN']}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    with urllib.request.urlopen(req) as resp:
        print(resp.read().decode())
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    use serde_json::json;

    let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
    let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
    let body = json!({
        "task_id": "00000000-0000-0000-0000-000000000000",
        "payload": { "refresh": true }
    });
    let resp = ureq::post(format!("{base}/api/v1/executions"))
        .set("Authorization", &format!("Bearer {token}"))
        .set("Content-Type", "application/json")
        .send_json(body)
        .expect("trigger");
    println!("{}", resp.into_string().expect("body"));
    ```
  </Tab>
</Tabs>

## Related topics

* [Action types overview](/about-platform/action-types/overview)
* [Scrape](/about-platform/action-types/scrape)
* [Authentication and API keys](/get-started/authentication-and-api-keys) for scopes on task and execution routes
