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

# Exemplos em JavaScript

> Integração com fetch, axios e Node.js

## Variáveis de ambiente

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
export SHAPPIRE_API_KEY=shp_live_SUA_CHAVE
```

## Cliente mínimo (fetch)

```javascript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
const API_KEY = process.env.SHAPPIRE_API_KEY;
const BASE = 'https://api.shappire.tools/v1';

async function shappireRequest(path, options = {}) {
  const res = await fetch(`${BASE}${path}`, {
    ...options,
    headers: {
      'X-API-Key': API_KEY,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  const body = await res.json();
  if (!res.ok) {
    throw new Error(body.error?.message ?? res.statusText);
  }
  return body.data;
}
```

## Resolver + download + polling

```javascript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
async function downloadFromUrl(url) {
  const media = await shappireRequest('/media/resolve', {
    method: 'POST',
    body: JSON.stringify({ url }),
  });

  const format = media.formats.find((f) => f.type === 'video')?.id ?? 'video_best';

  const queued = await fetch(`${BASE}/media/download`, {
    method: 'POST',
    headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ media_id: media.id, format }),
  });

  const { data: jobRef } = await queued.json();
  return waitForJob(jobRef.job_id);
}

async function waitForJob(jobId) {
  for (let i = 0; i < 100; i++) {
    const job = await shappireRequest(`/jobs/${jobId}`);
    if (job.status === 'completed') return job;
    if (job.status === 'failed') throw new Error(job.error?.message);
    await new Promise((r) => setTimeout(r, 3000));
  }
  throw new Error('timeout');
}
```

## Com axios

```javascript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import axios from 'axios';

const client = axios.create({
  baseURL: 'https://api.shappire.tools/v1',
  headers: { 'X-API-Key': process.env.SHAPPIRE_API_KEY },
});

const { data: media } = await client.post('/media/resolve', {
  url: 'https://www.tiktok.com/@user/video/7123456789012345678',
});

const { data: job } = await client.post('/media/download', {
  media_id: media.id,
  format: 'video_best',
});
```

## Tratamento de rate limit

```javascript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
async function withRetry(fn, max = 5) {
  for (let attempt = 0; attempt < max; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (!String(err.message).includes('Too many requests')) throw err;
      const reset = Number(err.resetAt ?? 60);
      await new Promise((r) => setTimeout(r, reset * 1000));
    }
  }
}
```

## Express.js (backend)

```javascript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import express from 'express';

const app = express();
app.post('/api/download', async (req, res) => {
  try {
    const job = await downloadFromUrl(req.body.url);
    res.json({ download_url: job.result.download_url });
  } catch (err) {
    res.status(502).json({ error: err.message });
  }
});
```

<Warning>
  Nunca chame a API Shappire direto do browser — a chave vazaria no cliente.
</Warning>
