AI-powered setup

Install denkbot.dog with your AI assistant

Copy this prompt into Claude, Cursor, ChatGPT, or any AI coding tool. It detects your framework, installs the right helper, and guides you to your API key.

PythonNode.js / TypeScriptRubyGoPHP

How it works

  1. 1

    Copy the prompt

    Click the button below to copy the full AI install prompt to your clipboard.

  2. 2

    Paste into your AI assistant

    Open Claude Code, Cursor, ChatGPT, Copilot, or any AI assistant and paste it.

  3. 3

    The AI installs denkbot.dog

    It detects your framework, creates the right helper utility, and adds the env var.

  4. 4

    Get your API key

    The AI walks you through getting your key at denkbot.dog/dashboard and where to put it.

Prompt preview

Install the denkbot.dog web scraping API into this codebase.

denkbot.dog is a REST API that fetches any URL, renders JavaScript-heavy pages (React/Vue/Angular), takes screenshots, crawls websites, and extracts sitemaps. €19/year — unlimited requests, no SDK, no Docker, just a POST request.

## What to do

1. Detect the language and framework used in this codebase
2. Create a denkbot.dog helper utility in the appropriate location
3. Use `DENKBOT_API_KEY` as the environment variable name
4. Add `DENKBOT_API_KEY=dk_live_your_key_here` to `.env.example` (and `.env`, `.env.local`, or the equivalent env file this project uses)

---

## API reference

Base URL: `https://api.denkbot.dog`
Auth header: `Authorization: Bearer $DENKBOT_API_KEY`

### POST /scrape — fetch any URL
JavaScript rendering is ON by default (Playwright/Chromium). Set `js: false` for faster static-only fetching.

Request body:
- `url` (string, required)
- `js` (boolean, default: true) — set false to skip Playwright (faster for static pages)
- `format` (string, default: "parsed") — "parsed" | "raw" | "both"
- `waitUntil` (string) — "load" | "domcontentloaded" | "networkidle"
- `no_cache` (boolean) — skip the 15-minute cache

Response structure:
```json
{
  "url": "...",
  "format": "parsed",
  "data": {
    "title": "...",
    "description": "...",
    "text": "...",
    "headings": ["..."],
    "links": [{ "href": "...", "text": "..." }],
    "images": [{ "src": "...", "alt": "..." }],
    "meta": {}
  },
  "duration_ms": 312,
  "cached": false
}
```

Use `data.text` for LLM consumption — clean plain text, no HTML parsing needed.

### POST /screenshot — screenshot any URL
Returns raw PNG bytes (no base64). Content-Type: image/png.
Request: `url` (required), `wait_until` (optional: "load" | "domcontentloaded" | "networkidle")

### POST /crawl — crawl an entire website
Returns a nested URL tree: `{ root, total_found, tree: { url, children: [...] } }`
Request: `url` (required), `limit` (default 100, max 500), `depth` (default 3, max 5)

### POST /sitemap — extract XML sitemap
Returns: `{ sitemap_url, total, urls: [{ loc, lastmod, changefreq, priority }] }`
Request: `url` (required), `limit` (default 500)

---

## Language-specific helpers

### Python (Django / Flask / FastAPI / script)

Create `denkbot.py` or add to an existing utils module:

```python
import httpx, os

_KEY = os.environ["DENKBOT_API_KEY"]
_BASE = "https://api.denkbot.dog"
_HEADERS = {"Authorization": f"Bearer {_KEY}", "Content-Type": "application/json"}

def scrape(url: str, *, js: bool = True, format: str = "parsed") -> dict:
    """Fetch any URL. Returns data.title, data.text, data.links, etc."""
    r = httpx.post(f"{_BASE}/scrape", headers=_HEADERS,
                   json={"url": url, "js": js, "format": format})
    r.raise_for_status()
    return r.json()

def screenshot(url: str) -> bytes:
    """Screenshot any URL. Returns raw PNG bytes."""
    r = httpx.post(f"{_BASE}/screenshot", headers=_HEADERS, json={"url": url})
    r.raise_for_status()
    return r.content

def crawl(url: str, *, limit: int = 100, depth: int = 3) -> dict:
    """Crawl a site and return a nested URL tree."""
    r = httpx.post(f"{_BASE}/crawl", headers=_HEADERS,
                   json={"url": url, "limit": limit, "depth": depth})
    r.raise_for_status()
    return r.json()

def sitemap(url: str) -> list[str]:
    """Return all URLs from a domain's XML sitemap."""
    r = httpx.post(f"{_BASE}/sitemap", headers=_HEADERS, json={"url": url})
    r.raise_for_status()
    return [u["loc"] for u in r.json().get("urls", [])]
```

Add `httpx` to requirements if not already present.

---

### Node.js / TypeScript (Express / Next.js / Hono / any)

Create `lib/denkbot.ts` (or `utils/denkbot.js`):

```typescript
const KEY = process.env.DENKBOT_API_KEY!
const BASE = 'https://api.denkbot.dog'
const H = { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' }

async function post(path: string, body: object) {
  const r = await fetch(`${BASE}${path}`, { method: 'POST', headers: H, body: JSON.stringify(body) })
  if (!r.ok) throw new Error(`denkbot ${r.status}: ${await r.text()}`)
  return r
}

export async function scrape(url: string, opts: { js?: boolean; format?: string } = {}) {
  return (await post('/scrape', { url, js: true, ...opts })).json()
  // result.data.title, result.data.text, result.data.links, etc.
}

export async function screenshot(url: string): Promise<Buffer> {
  return Buffer.from(await (await post('/screenshot', { url })).arrayBuffer())
}

export async function crawl(url: string, opts: { limit?: number; depth?: number } = {}) {
  return (await post('/crawl', { url, limit: 100, ...opts })).json()
}

export async function sitemap(url: string): Promise<string[]> {
  const d = await (await post('/sitemap', { url })).json()
  return d.urls?.map((u: { loc: string }) => u.loc) ?? []
}
```

---

### Ruby

Create `lib/denkbot.rb`:

```ruby
require 'net/http'
require 'json'

module Denkbot
  BASE = 'https://api.denkbot.dog'
  KEY  = ENV.fetch('DENKBOT_API_KEY')

  def self.post(path, body)
    uri = URI("#{BASE}#{path}")
    req = Net::HTTP::Post.new(uri,
      'Content-Type'  => 'application/json',
      'Authorization' => "Bearer #{KEY}")
    req.body = body.to_json
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    JSON.parse(res.body)
  end

  def self.scrape(url, js: true) = post('/scrape', { url:, js: })
  def self.screenshot(url) = post('/screenshot', { url: })
  def self.crawl(url, limit: 100, depth: 3) = post('/crawl', { url:, limit:, depth: })
  def self.sitemap(url) = post('/sitemap', { url: })['urls']&.map { _1['loc'] } || []
end
```

---

### Go

Create `internal/denkbot/denkbot.go`:

```go
package denkbot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

var (
	key  = os.Getenv("DENKBOT_API_KEY")
	base = "https://api.denkbot.dog"
)

func post(path string, body any) ([]byte, error) {
	b, _ := json.Marshal(body)
	req, _ := http.NewRequest("POST", base+path, bytes.NewReader(b))
	req.Header.Set("Authorization", "Bearer "+key)
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("denkbot error %d", resp.StatusCode)
	}
	return io.ReadAll(resp.Body)
}

func Scrape(url string, js bool) (map[string]any, error) {
	b, err := post("/scrape", map[string]any{"url": url, "js": js, "format": "parsed"})
	if err != nil {
		return nil, err
	}
	var out map[string]any
	return out, json.Unmarshal(b, &out)
}

func Screenshot(url string) ([]byte, error) {
	return post("/screenshot", map[string]any{"url": url})
}

func Crawl(url string, limit int) (map[string]any, error) {
	b, err := post("/crawl", map[string]any{"url": url, "limit": limit})
	if err != nil {
		return nil, err
	}
	var out map[string]any
	return out, json.Unmarshal(b, &out)
}
```

---

### PHP

Create `denkbot.php`:

```php
<?php

function denkbot_post(string $path, array $body): array {
    $ch = curl_init("https://api.denkbot.dog$path");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $_ENV['DENKBOT_API_KEY'],
        ],
        CURLOPT_POSTFIELDS => json_encode($body),
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $result = curl_exec($ch);
    curl_close($ch);
    return json_decode($result, true);
}

function denkbot_scrape(string $url, bool $js = true): array {
    return denkbot_post('/scrape', ['url' => $url, 'js' => $js, 'format' => 'parsed']);
}

function denkbot_screenshot(string $url): string {
    return file_get_contents('https://api.denkbot.dog/screenshot?url=' . urlencode($url));
}

function denkbot_crawl(string $url, int $limit = 100): array {
    return denkbot_post('/crawl', ['url' => $url, 'limit' => $limit]);
}

function denkbot_sitemap(string $url): array {
    $result = denkbot_post('/sitemap', ['url' => $url]);
    return array_column($result['urls'] ?? [], 'loc');
}
```

---

## When to use each endpoint

| Need | Endpoint | Key option |
|------|----------|------------|
| Page content / text for LLM | POST /scrape | — |
| Faster static-only fetch | POST /scrape | js: false |
| Visual screenshot | POST /screenshot | — |
| All internal pages of a site | POST /crawl | limit, depth |
| All URLs in XML sitemap | POST /sitemap | limit |

---

## Finishing up — API key setup

Tell the user the following (do not skip this step, do not call any denkbot.dog endpoint before the key is set):

> **Integration complete!** One last step — you need your API key to activate denkbot.dog:
>
> 1. Go to **https://denkbot.dog/dashboard** and sign in (or create an account — it takes 30 seconds)
> 2. Copy your API key — it starts with `dk_live_`
> 3. Add it to your environment:
>    - In `.env` or `.env.local`: `DENKBOT_API_KEY=dk_live_...`
>    - In shell: `export DENKBOT_API_KEY=dk_live_...`
>    - In production: add it to your hosting platform's environment variables (Vercel, Railway, Fly.io, Heroku, etc.)
>
> Once the key is set, test it:
> ```bash
> curl -X POST https://api.denkbot.dog/scrape \
>   -H "Authorization: Bearer $DENKBOT_API_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{"url": "https://example.com", "js": false}'
> ```
>
> You should get back a JSON object with `data.title`, `data.text`, and `data.links`.
>
> **Pricing:** €19/year — unlimited requests, 15-minute cache, no per-call fees.

Do **not** proceed to demonstrate or call the API until the user confirms `DENKBOT_API_KEY` is in place.

Need your API key first?

€19/year, unlimited requests, no per-call fees