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

# Content Endpoint Guide

> Extract clean, structured content from web pages

Turn any web page into clean, structured data. The Contents API extracts content from URLs with batch processing, AI-powered summaries, and structured outputs.

## What You Can Do

* **Feed your AI** - Clean data without noise
* **Aggregate content** - Extract structured data from multiple sources
* **Transform content** - Convert web pages into usable formats
* **Automate research** - Pull key information from articles, papers, and reports

## Features

<CardGroup cols={2}>
  <Card title="Batch Processing" icon="layer-group">
    Submit up to 10 URLs synchronously, or up to 50 URLs with async mode.
  </Card>

  <Card title="AI-Powered Structuring" icon="brain">
    Use JSON schemas to extract specific data points.
  </Card>

  <Card title="Smart Summarisation" icon="wand-magic-sparkles">
    Generate tailored summaries with custom instructions.
  </Card>

  <Card title="Pay-per-Success" icon="dollar-sign">
    Only pay for URLs that are successfully processed.
  </Card>
</CardGroup>

## Getting Started

### Basic Extraction

<CodeGroup>
  ```python Python theme={null}
  from valyu import Valyu

  valyu = Valyu()  # Uses VALYU_API_KEY from env

  data = valyu.contents(
      urls=[
          "https://en.wikipedia.org/wiki/Artificial_intelligence",
      ],
      response_length="medium",
      extract_effort="auto",
  )
  print(data["results"][0]["content"][:500])
  ```

  ```javascript JavaScript theme={null}
  import { Valyu } from "valyu-js";

  const valyu = new Valyu(); // Uses VALYU_API_KEY from env

  const data = await valyu.contents({
    urls: [
      "https://en.wikipedia.org/wiki/Artificial_intelligence",
    ],
    responseLength: "medium",
    maxPriceDollars: 0.1,
    extractEffort: "auto",
  });
  console.log(data.results[0].content.slice(0, 500));
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.valyu.ai/v1/contents \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "urls": [
        "https://en.wikipedia.org/wiki/Artificial_intelligence"
      ],
      "response_length": "medium",
      "extract_effort": "auto"
    }'
  ```
</CodeGroup>

Returns clean markdown content for each URL.

### Response Length

| **Length**     | **Characters**  | **Use for**                        |
| -------------- | --------------- | ---------------------------------- |
| `short`        | 25,000          | Summaries, key points              |
| `medium`       | 50,000          | Articles, blog posts               |
| `large`        | 100,000         | Academic papers, long-form content |
| `max`          | Unlimited       | Full document extraction           |
| Custom integer | 1,000-1,000,000 | Specific requirements              |

### Extract Effort

| **Effort** | **Description**                       |
| ---------- | ------------------------------------- |
| `normal`   | Standard speed and quality (default)  |
| `high`     | Better quality, slower                |
| `auto`     | Automatically chooses the right level |

### Screenshot Capture

Capture visual screenshots of pages alongside content extraction:

<CodeGroup>
  ```python Python theme={null}
  from valyu import Valyu

  valyu = Valyu()

  data = valyu.contents(
      urls=["https://example.com/article"],
      extract_effort="auto",
      screenshot=True,
  )
  print(data["results"][0]["screenshot_url"])
  ```

  ```javascript JavaScript theme={null}
  import { Valyu } from "valyu-js";

  const valyu = new Valyu();

  const data = await valyu.contents({
    urls: ["https://example.com/article"],
    extractEffort: "auto",
    screenshot: true,
  });
  console.log(data.results[0].screenshot_url);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.valyu.ai/v1/contents \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "urls": ["https://example.com/article"],
      "extract_effort": "auto",
      "screenshot": true
    }'
  ```
</CodeGroup>

<Note>
  Screenshots are captured during page rendering and returned as pre-signed URLs. PDF files do not support screenshots.
</Note>

## Advanced Features

### Summary Options

The `summary` field accepts four types of values:

#### No AI Processing (`false`)

<CodeGroup>
  ```python Python theme={null}
  from valyu import Valyu

  valyu = Valyu()

  data = valyu.contents(
      urls=["https://example.com/article"],
      extract_effort="normal",
      summary=False,
  )
  print(data["results"][0]["content"][:300])
  ```

  ```javascript JavaScript theme={null}
  import { Valyu } from "valyu-js";

  const valyu = new Valyu();

  const data = await valyu.contents({
    urls: ["https://example.com/article"],
    maxPriceDollars: 0.1,
    extractEffort: "normal",
    summary: false,
  });
  console.log(data.results[0].content.slice(0, 300));
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.valyu.ai/v1/contents \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "urls": ["https://example.com/article"],
      "extract_effort": "normal", 
      "summary": false
    }'
  ```
</CodeGroup>

#### Basic Summary (`true`)

<CodeGroup>
  ```python Python theme={null}
  from valyu import Valyu

  valyu = Valyu()

  data = valyu.contents(
      urls=["https://example.com/article"],
      extract_effort="auto",
      summary=True,
  )
  print(data["results"][0]["content"])
  ```

  ```javascript JavaScript theme={null}
  import { Valyu } from "valyu-js";

  const valyu = new Valyu();

  const data = await valyu.contents({
    urls: ["https://example.com/article"],
    maxPriceDollars: 0.1,
    extractEffort: "auto",
    summary: true,
  });
  console.log(data.results[0].content);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.valyu.ai/v1/contents \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "urls": ["https://example.com/article"],
      "extract_effort": "auto",
      "summary": true
    }'
  ```
</CodeGroup>

#### Custom Instructions (`string`)

<CodeGroup>
  ```python Python theme={null}
  from valyu import Valyu

  valyu = Valyu()

  data = valyu.contents(
      urls=["https://example.com/research-paper"],
      extract_effort="auto",
      summary="Summarise the methodology, key findings, and practical applications in 2-3 paragraphs",
  )
  print(data["results"][0]["content"])
  ```

  ```javascript JavaScript theme={null}
  import { Valyu } from "valyu-js";

  const valyu = new Valyu();

  const data = await valyu.contents({
    urls: ["https://example.com/research-paper"],
    extractEffort: "auto",
    summary: "Summarise the methodology, key findings, and practical applications in 2-3 paragraphs",
  });
  console.log(data.results[0].content);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.valyu.ai/v1/contents \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "urls": ["https://example.com/research-paper"],
      "extract_effort": "auto", 
      "summary": "Summarise the methodology, key findings, and practical applications in 2-3 paragraphs"
    }'
  ```
</CodeGroup>

#### Structured Extraction (`object`)

<CodeGroup>
  ```python Python theme={null}
  from valyu import Valyu

  valyu = Valyu()

  data = valyu.contents(
      urls=["https://example.com/product-page"],
      extract_effort="auto",
      summary={
          "type": "object",
          "properties": {
              "product_name": {"type": "string", "description": "Name of the product"},
              "price": {"type": "number", "description": "Product price in USD"},
              "features": {
                  "type": "array",
                  "items": {"type": "string"},
                  "maxItems": 5,
                  "description": "Key product features",
              },
              "availability": {
                  "type": "string",
                  "enum": ["in_stock", "out_of_stock", "preorder"],
                  "description": "Product availability status",
              },
          },
          "required": ["product_name", "price"],
      },
  )
  print(data["results"][0]["content"])
  ```

  ```javascript JavaScript theme={null}
  import { Valyu } from "valyu-js";

  const valyu = new Valyu();

  const data = await valyu.contents({
    urls: ["https://example.com/product-page"],
    maxPriceDollars: 0.1,
    extractEffort: "auto",
    summary: {
      type: "object",
      properties: {
        product_name: { type: "string", description: "Name of the product" },
        price: { type: "number", description: "Product price in USD" },
        features: {
          type: "array",
          items: { type: "string" },
          maxItems: 5,
          description: "Key product features",
        },
        availability: {
          type: "string",
          enum: ["in_stock", "out_of_stock", "preorder"],
          description: "Product availability status",
        },
      },
      required: ["product_name", "price"],
    },
  });
  console.log(data.results[0].content);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.valyu.ai/v1/contents \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "urls": ["https://example.com/product-page"],
      "extract_effort": "auto",
      "summary": {
        "type": "object",
        "properties": {
          "product_name": {
            "type": "string",
            "description": "Name of the product"
          },
          "price": {
            "type": "number",
            "description": "Product price in USD"
          },
          "features": {
            "type": "array",
            "items": {"type": "string"},
            "maxItems": 5,
            "description": "Key product features"
          },
          "availability": {
            "type": "string",
            "enum": ["in_stock", "out_of_stock", "preorder"],
            "description": "Product availability status"
          }
        },
        "required": ["product_name", "price"]
      }
    }'
  ```
</CodeGroup>

### JSON Schema Reference

For structured extraction, you can use any valid JSON Schema. See the [JSON Schema Type Reference](https://json-schema.org/understanding-json-schema/reference/type) for details.

**Limits:**

* 5,000 characters max
* 3 levels deep max
* 20 properties per object max

**Common types:**

* `string` - Text with optional format validation
* `number` / `integer` - Numbers with optional min/max
* `boolean` - True/false
* `array` - Lists with optional size limits
* `object` - Nested structures

## Async Processing

For large-scale extraction (11-50 URLs) or non-blocking workflows, use async mode. Submit URLs, get a `job_id` back immediately, then either poll for results or receive them via webhook.

### When to use async

* **More than 10 URLs** — required for batches of 11-50 URLs
* **Non-blocking workflows** — submit and continue processing while extraction runs in the background
* **Webhook-driven architectures** — receive results via webhook instead of polling
* **Extended processing** — async mode provides 120s timeout per URL vs 25s for sync

<Note>
  Async mode is **required** when submitting more than 10 URLs. For 1-10 URLs, you can optionally use async mode for non-blocking workflows.
</Note>

### Quick start

<CodeGroup>
  ```python Python theme={null}
  from valyu import Valyu

  valyu = Valyu()

  # Submit async job — returns immediately with a job_id
  job = valyu.contents(
      urls=[
          "https://example.com/page1",
          "https://example.com/page2",
          "https://example.com/page3",
          # ... up to 50 URLs
      ],
      async_mode=True,
      webhook_url="https://your-app.com/webhooks/valyu",  # optional
  )

  print(f"Job ID: {job['job_id']}")

  # Option 1: Block until complete (SDK handles polling)
  result = valyu.wait_for_contents_job(
      job["job_id"],
      poll_interval=5,        # seconds between polls
      max_wait_time=3600,     # max seconds to wait
      on_progress=lambda s: print(f"  {s['status']} — batch {s.get('current_batch', '?')}/{s.get('total_batches', '?')}"),
  )

  # Option 2: Or pass wait=True when submitting to auto-poll
  result = valyu.contents(
      urls=["https://example.com/page1", "https://example.com/page2"],
      async_mode=True,
      wait=True,  # blocks until job completes
  )

  for r in result["results"]:
      print(f"{r['title']}: {r['length']} characters")
  print(f"Total cost: ${result['actual_cost_dollars']}")
  ```

  ```javascript JavaScript theme={null}
  import { Valyu } from "valyu-js";

  const valyu = new Valyu();

  // Submit async job — returns immediately with a job_id
  const job = await valyu.contents([
    "https://example.com/page1",
    "https://example.com/page2",
    "https://example.com/page3",
    // ... up to 50 URLs
  ], {
    asyncMode: true,
    webhookUrl: "https://your-app.com/webhooks/valyu", // optional
  });

  console.log(`Job ID: ${job.job_id}`);

  // Block until complete (SDK handles polling)
  const result = await valyu.waitForJob(job.job_id, {
    pollInterval: 5000,       // ms between polls
    maxWaitTime: 7200000,     // max ms to wait
    onProgress: (s) => console.log(`  ${s.status} — batch ${s.current_batch ?? "?"}/${s.total_batches ?? "?"}`),
  });

  result.results?.forEach(r => {
    console.log(`${r.title}: ${r.length} characters`);
  });
  console.log(`Total cost: $${result.actual_cost_dollars}`);
  ```

  ```bash cURL theme={null}
  # Submit async job
  curl -X POST https://api.valyu.ai/v1/contents \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "urls": [
        "https://example.com/page1",
        "https://example.com/page2",
        "https://example.com/page3"
      ],
      "async": true,
      "webhook_url": "https://your-app.com/webhooks/valyu"
    }'

  # Poll for results
  curl https://api.valyu.ai/v1/contents/jobs/cj_a1b2c3d4e5f6g7h8 \
    -H "x-api-key: YOUR_API_KEY"
  ```
</CodeGroup>

<Warning>
  The `webhook_secret` is **only** returned in the initial 202 response. Store it immediately — you cannot retrieve it later.
</Warning>

### Initial response (HTTP 202)

```json theme={null}
{
  "success": true,
  "job_id": "cj_a1b2c3d4e5f6g7h8",
  "status": "pending",
  "urls_total": 25,
  "poll_url": "/contents/jobs/cj_a1b2c3d4e5f6g7h8",
  "tx_id": "tx_async-1234-5678-abcd-ef0123456789",
  "webhook_secret": "a1b2c3d4e5f6..."
}
```

### Manual polling

If you prefer to poll manually instead of using the SDK convenience methods:

<CodeGroup>
  ```python Python theme={null}
  import time

  while True:
      status = valyu.get_contents_job(job["job_id"])
      print(f"Status: {status['status']} ({status.get('current_batch', '?')}/{status.get('total_batches', '?')} batches)")

      if status["status"] in ("completed", "partial", "failed"):
          break
      time.sleep(2)

  if status["status"] in ("completed", "partial"):
      for result in status["results"]:
          print(f"{result['title']}: {result['length']} characters")

  if status["status"] in ("partial", "failed"):
      print(f"Error: {status.get('error', 'Unknown')}")
  ```

  ```javascript JavaScript theme={null}
  let status;
  do {
    status = await valyu.getContentsJob(job.job_id);
    console.log(`Status: ${status.status}`);

    if (!["completed", "partial", "failed"].includes(status.status)) {
      await new Promise((r) => setTimeout(r, 2000));
    }
  } while (!["completed", "partial", "failed"].includes(status.status));

  if (status.status === "completed" || status.status === "partial") {
    for (const result of status.results) {
      console.log(`${result.title}: ${result.length} characters`);
    }
  }
  ```
</CodeGroup>

### Job lifecycle

Jobs progress through these statuses:

| **Status**   | **Description**                      |
| ------------ | ------------------------------------ |
| `pending`    | Job created, not yet started         |
| `processing` | URLs being processed in batches of 5 |
| `completed`  | All URLs processed successfully      |
| `partial`    | Finished with some URL failures      |
| `failed`     | All URLs failed                      |

### Job status fields

| **Field**             | **Type** | **Description**                    | **Present**             |
| --------------------- | -------- | ---------------------------------- | ----------------------- |
| `job_id`              | string   | Job identifier (prefixed `cj_`)    | Always                  |
| `status`              | string   | Current job status (see above)     | Always                  |
| `urls_total`          | number   | Total URLs submitted               | Always                  |
| `urls_processed`      | number   | URLs successfully processed        | Always                  |
| `urls_failed`         | number   | URLs that failed processing        | Always                  |
| `created_at`          | number   | Job creation time (ms since epoch) | Always                  |
| `updated_at`          | number   | Last update time (ms since epoch)  | Always                  |
| `current_batch`       | number   | Current batch being processed      | `processing` only       |
| `total_batches`       | number   | Total number of batches            | `processing` only       |
| `results`             | array    | Extraction results                 | `completed`/`partial`   |
| `actual_cost_dollars` | number   | Total cost in dollars              | `completed`/`partial`   |
| `error`               | string   | Error description                  | `partial`/`failed` only |

### Webhooks

When you provide a `webhook_url`, Valyu sends a POST request to your endpoint when the job completes (or partially completes/fails).

**Headers:**

| **Header**            | **Value**                 |
| --------------------- | ------------------------- |
| `Content-Type`        | `application/json`        |
| `User-Agent`          | `Valyu-Contents/1.0`      |
| `X-Webhook-Signature` | `sha256={hex_digest}`     |
| `X-Webhook-Timestamp` | Unix timestamp in seconds |

**Payload:**

```json theme={null}
{
  "job_id": "cj_a1b2c3d4e5f6g7h8",
  "status": "completed",
  "urls_total": 25,
  "urls_processed": 25,
  "urls_failed": 0,
  "results": [...],
  "actual_cost_dollars": 0.025,
  "error": null
}
```

**Retries:** Max 5 attempts with exponential backoff (1s, 2s, 4s, 8s). No retry on 4xx. 5s connect timeout, 15s read timeout.

#### Verifying webhook signatures

The payload is signed using HMAC-SHA256. The signed payload is the timestamp and JSON body joined by a period: `"{timestamp}.{json_payload}"`.

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_webhook(
      payload: bytes,
      signature_header: str,
      timestamp_header: str,
      webhook_secret: str
  ) -> bool:
      signed_payload = f"{timestamp_header}.{payload.decode('utf-8')}"

      expected = hmac.new(
          webhook_secret.encode("utf-8"),
          signed_payload.encode("utf-8"),
          hashlib.sha256
      ).hexdigest()

      received = signature_header.removeprefix("sha256=")
      return hmac.compare_digest(expected, received)
  ```

  ```javascript JavaScript theme={null}
  import crypto from "crypto";

  function verifyWebhook(payload, signatureHeader, timestampHeader, webhookSecret) {
    const signedPayload = `${timestampHeader}.${payload}`;

    const expected = crypto
      .createHmac("sha256", webhookSecret)
      .update(signedPayload)
      .digest("hex");

    const received = signatureHeader.replace("sha256=", "");

    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(received)
    );
  }
  ```
</CodeGroup>

<Tip>
  The TypeScript SDK exports a `verifyContentsWebhookSignature()` helper that handles this for you.
</Tip>

### Limits and pricing

| **Detail**                   | **Value**        |
| ---------------------------- | ---------------- |
| Max URLs (sync)              | 10               |
| Max URLs (async)             | 50               |
| Batch size                   | 5 URLs per batch |
| Timeout per URL (sync)       | 25 seconds       |
| Timeout per URL (async)      | 120 seconds      |
| Base pricing                 | \$0.001 per URL  |
| AI features (summary/schema) | +\$0.001 per URL |
| Job expiry (TTL)             | 7 days           |

## Examples

### News Aggregator

Extract structured article data:

```json theme={null}
{
  "urls": [
    "https://en.wikipedia.org/wiki/Artificial_intelligence",
    "https://archive.org/details/texts",
    "https://www.gov.uk/search/news"
  ],
  "extract_effort": "auto",
  "summary": {
    "type": "object",
    "properties": {
      "headline": { "type": "string" },
      "summary_text": { "type": "string" },
      "category": { "type": "string" },
      "tags": {
        "type": "array",
        "items": { "type": "string" },
        "maxItems": 5
      }
    },
    "required": ["headline"]
  }
}
```

### Research Paper

Extract structured academic data:

```json theme={null}
{
  "urls": ["https://arxiv.org/paper/example"],
  "response_length": "max",
  "extract_effort": "high",
  "summary": {
    "type": "object",
    "properties": {
      "title": { "type": "string" },
      "abstract": { "type": "string" },
      "methodology": { "type": "string" },
      "key_findings": {
        "type": "array",
        "items": { "type": "string" }
      },
      "limitations": { "type": "string" }
    },
    "required": ["title"]
  }
}
```

### Product Info

Extract product data:

```json theme={null}
{
  "urls": ["https://company.com/product-A", "https://company.com/product-B"],
  "extract_effort": "auto",
  "summary": {
    "type": "object",
    "properties": {
      "product_name": { "type": "string" },
      "features": {
        "type": "array",
        "items": { "type": "string" }
      },
      "pricing": { "type": "string" },
      "target_audience": { "type": "string" }
    },
    "required": ["product_name"]
  }
}
```

## Response Format

### Raw Content (summary: false)

```json theme={null}
{
  "success": true,
  "error": null,
  "tx_id": "tx_12345678-1234-1234-1234-123456789abc",
  "results": [
    {
      "title": "AI Breakthrough in Natural Language Processing",
      "url": "https://example.com/article?utm_source=valyu",
      "content": "# AI Breakthrough in Natural Language Processing\n\nPage content in markdown...",
      "description": "Latest AI developments",
      "source": "web",
      "price": 0.001,
      "length": 12840,
      "data_type": "unstructured",
      "image_url": {
        "main": "https://example.com/hero-image.jpg"
      }
    }
  ],
  "urls_requested": 1,
  "urls_processed": 1,
  "urls_failed": 0,
  "total_cost_dollars": 0.001,
  "total_characters": 12840
}
```

### Summary (summary: true or string)

```json theme={null}
{
  "success": true,
  "results": [
    {
      "title": "AI Breakthrough in Natural Language Processing",
      "content": "This article discusses a breakthrough in AI...",
      "summary_success": true,
      "price": 0.002,
      "data_type": "unstructured"
    }
  ]
}
```

### Structured (JSON Schema)

```json theme={null}
{
  "success": true,
  "results": [
    {
      "title": "AI Breakthrough in Natural Language Processing",
      "content": {
        "title": "AI Breakthrough in Natural Language Processing",
        "author": "John Doe",
        "category": "technology",
        "key_points": [
          "New AI model achieves 95% accuracy",
          "Reduces computational requirements by 40%"
        ]
      },
      "summary_success": true,
      "price": 0.002,
      "data_type": "structured"
    }
  ]
}
```

### Response Fields

| **Field**         | **Description**                                                               |
| ----------------- | ----------------------------------------------------------------------------- |
| `title`           | Extracted page title                                                          |
| `url`             | Original URL with UTM tracking parameters                                     |
| `content`         | Extracted content (markdown or JSON)                                          |
| `description`     | Page meta description or excerpt                                              |
| `source`          | Source type - always "web" for URL processing                                 |
| `price`           | Cost for processing this URL in dollars                                       |
| `length`          | Character count of extracted content                                          |
| `data_type`       | `"unstructured"` or `"structured"`                                            |
| `summary_success` | Whether AI processing succeeded (only when `summary` parameter is used)       |
| `image_url`       | Dictionary of extracted image URLs                                            |
| `screenshot_url`  | Pre-signed URL to page screenshot (only when `screenshot=true` was requested) |

## Best Practices

### Choosing Summary Type

* `false`: Fastest and cheapest—no AI
* `true`: Basic summary for overviews
* `"string"`: Custom instructions for specific needs
* `{object}`: Structured extraction for data processing

### JSON Schema Tips

* Use clear descriptions to guide extraction
* Use enums for consistent categorisation
* Keep schemas under 3 levels deep
* Mark essential fields as required

### Batch Processing

* Group similar content types together
* Choose appropriate response length
* Check `summary_success` for AI status
* Track `total_cost_dollars`

### Error Handling

```python theme={null}
# Check for partial failures (HTTP 206)
if response.status_code == 206:
    successful_results = [r for r in response.json()["results"]]
    failed_count = response.json()["urls_failed"]

# Check AI processing success
for result in results:
    if "summary" in result and "summary_success" in result:
        if not result["summary_success"]:
            print(f"AI processing failed for {result['url']}")

# Handle complete failures (HTTP 422)
if response.status_code == 422:
    error_message = response.json()["error"]
```

<Card title="Try the Contents API" icon="rocket" href="/api-reference/endpoint/contents">
  Full API reference with interactive examples
</Card>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/api-reference/endpoint/contents">
    Complete parameter documentation
  </Card>

  <Card title="Python SDK" icon="python" href="/sdk/python-sdk">
    Python integration
  </Card>

  <Card title="TypeScript SDK" icon="js" href="/sdk/typescript-sdk">
    TypeScript integration
  </Card>

  <Card title="Integrations" icon="plug" href="/integrations/langchain">
    LangChain, LlamaIndex, and more
  </Card>
</CardGroup>
