# Code samples

Copy-paste examples in cURL, Node.js, and Python. Replace credentials and the host as needed.
All examples assume `https://api.kwery.co`.

## Submit a batch job

### cURL

```bash
curl -s https://api.kwery.co/job \
  -H "Authorization: Bearer $KWERY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source":"idealo","country":"de","key":"gtin","values":["4006381333962"]}'
```

### Node.js

```js
const res = await fetch('https://api.kwery.co/job', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.KWERY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ source: 'idealo', country: 'de', key: 'gtin', values: ['4006381333962'] }),
});
const data = await res.json();
if (data.error) throw new Error(data.message);
console.log('job id:', data.job._id);
```

### Python

```python
import os, requests

res = requests.post(
    "https://api.kwery.co/job",
    headers={"Authorization": f"Bearer {os.environ['KWERY_API_KEY']}"},
    json={"source": "idealo", "country": "de", "key": "gtin", "values": ["4006381333962"]},
)
data = res.json()
if data.get("error"):
    raise RuntimeError(data["message"])
print("job id:", data["job"]["_id"])
```

## Poll, then download

### cURL

```bash
JOB=64f1c2d3e4b5a6c7d8e9f0a1
curl -s "https://api.kwery.co/job/$JOB"          -H "Authorization: Bearer $KWERY_API_KEY"   # status
curl -s "https://api.kwery.co/job/$JOB/download" -H "Authorization: Bearer $KWERY_API_KEY"   # results
```

### Node.js

```js
async function waitAndDownload(jobId) {
  const headers = { 'Authorization': `Basic ${auth}` };
  for (;;) {
    const job = await (await fetch(`https://api.kwery.co/job/${jobId}`, { headers })).json();
    if (job.status === 'finished') break;
    await new Promise(r => setTimeout(r, 3000));
  }
  return (await fetch(`https://api.kwery.co/job/${jobId}/download`, { headers })).json();
}
```

### Python

```python
import time

def wait_and_download(job_id, auth):
    while True:
        job = requests.get(f"https://api.kwery.co/job/{job_id}", auth=auth).json()
        if job.get("status") == "finished":
            break
        time.sleep(3)
    return requests.get(f"https://api.kwery.co/job/{job_id}/download", auth=auth).json()
```

## Submit a stream job

### cURL

```bash
curl -s https://api.kwery.co/stream \
  -H "Authorization: Bearer $KWERY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source":"idealo","country":"de","key":"gtin",
    "values":["4006381333962","4719512101148"],
    "callback_url":"https://your-app.example.com/kwery/webhook"
  }'
# → { "error": false, "job_id": "...", "webhook_secret": "..." }
```

Store the returned `webhook_secret` — you need it to verify deliveries.

## Verify a webhook delivery

The signature is `v1=` followed by the HMAC-SHA256 of `"{timestamp}.{raw_body}"`, keyed with
your `webhook_secret`. **Verify against the raw body, before JSON parsing.**

### Node.js (Express)

```js
const express = require('express');
const crypto = require('crypto');

const SECRET = process.env.KWERY_WEBHOOK_SECRET;  // from the POST /stream response
const app = express();

// Capture the raw body for signature verification.
app.use('/kwery/webhook', express.raw({ type: 'application/json' }));

app.post('/kwery/webhook', (req, res) => {
  const rawBody = req.body;                                   // Buffer
  const sig     = req.get('X-Kwery-Signature') || '';
  const ts      = req.get('X-Kwery-Timestamp') || '';

  // 1. Reject stale deliveries (replay protection).
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
    return res.status(401).end('stale timestamp');
  }

  // 2. Recompute and compare in constant time.
  const expected = 'v1=' + crypto.createHmac('sha256', SECRET)
    .update(`${ts}.${rawBody.toString('utf8')}`)
    .digest('hex');
  const ok = sig.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  if (!ok) return res.status(401).end('bad signature');

  // 3. Acknowledge fast, then process asynchronously.
  res.status(202).end('ok');

  const payload = JSON.parse(rawBody.toString('utf8'));
  for (const item of payload.results || []) {
    // handle item.result.content ...
  }
});

app.listen(9000);
```

### Python (Flask)

```python
import hmac, hashlib, time, os
from flask import Flask, request

SECRET = os.environ["KWERY_WEBHOOK_SECRET"].encode()
app = Flask(__name__)

@app.post("/kwery/webhook")
def webhook():
    raw = request.get_data()                       # raw bytes
    sig = request.headers.get("X-Kwery-Signature", "")
    ts  = request.headers.get("X-Kwery-Timestamp", "")

    # 1. Replay protection.
    if abs(time.time() - float(ts or 0)) > 300:
        return "stale timestamp", 401

    # 2. Constant-time signature check.
    expected = "v1=" + hmac.new(
        SECRET, f"{ts}.{raw.decode()}".encode(), hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(sig, expected):
        return "bad signature", 401

    # 3. Acknowledge, then process.
    payload = request.get_json()
    for item in payload.get("results", []):
        pass  # handle item["result"]["content"] ...
    return "ok", 202
```

## See also

- [Webhooks](/guides/webhooks) — payload shape, retries, replay, and polling fallback.
- [Authentication](/guides/authentication) — Basic vs Bearer.