# API quickstart

Canonical URL: https://docs.viralspy.com/docs/quickstart



## 1. Reveal your default key [#1-reveal-your-default-key]

Every organisation membership has a one-time **Default** key slot. Open [API settings](https://app.viralspy.com/settings/api), confirm the organisation shown, then reveal the key. Store it in a secret manager; ViralSpy only stores an HMAC digest and cannot show the secret again.

```bash
export VIRALSPY_API_KEY='vsp_live_…'
```

## 2. Search videos [#2-search-videos]

<Tabs items="[&#x22;curl&#x22;, &#x22;JavaScript&#x22;, &#x22;Python&#x22;, &#x22;PHP&#x22;, &#x22;Go&#x22;]">
  <Tab value="curl">
    ```bash
    curl --get 'https://api.viralspy.com/v1/videos' \
      --header "Authorization: Bearer $VIRALSPY_API_KEY" \
      --data-urlencode 'q=protein snack' \
      --data-urlencode 'hook=yes' \
      --data-urlencode 'sort=trending' \
      --data-urlencode 'limit=5'
    ```
  </Tab>

  <Tab value="JavaScript">
    ```js
    const url = new URL('https://api.viralspy.com/v1/videos');
    url.search = new URLSearchParams({ q: 'protein snack', hook: 'yes', sort: 'trending', limit: '5' });

    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.VIRALSPY_API_KEY}` },
    });
    if (!response.ok) throw new Error(await response.text());
    const page = await response.json();
    console.log(page.data, page.meta.next_cursor);
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    response = requests.get(
        "https://api.viralspy.com/v1/videos",
        headers={"Authorization": f"Bearer {os.environ['VIRALSPY_API_KEY']}"},
        params={"q": "protein snack", "hook": "yes", "sort": "trending", "limit": 5},
        timeout=30,
    )
    response.raise_for_status()
    page = response.json()
    print(page["data"], page["meta"]["next_cursor"])
    ```
  </Tab>

  <Tab value="PHP">
    ```php
    <?php
    $query = http_build_query(['q' => 'protein snack', 'hook' => 'yes', 'sort' => 'trending', 'limit' => 5]);
    $context = stream_context_create(['http' => ['header' => "Authorization: Bearer " . getenv('VIRALSPY_API_KEY')]]);
    $page = json_decode(file_get_contents("https://api.viralspy.com/v1/videos?$query", false, $context), true);
    print_r($page['data']);
    ```
  </Tab>

  <Tab value="Go">
    ```go
    req, _ := http.NewRequest("GET", "https://api.viralspy.com/v1/videos?q=protein+snack&hook=yes&limit=5", nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("VIRALSPY_API_KEY"))
    res, err := http.DefaultClient.Do(req)
    if err != nil { log.Fatal(err) }
    defer res.Body.Close()

    var page map[string]any
    if err := json.NewDecoder(res.Body).Decode(&page); err != nil { log.Fatal(err) }
    ```
  </Tab>
</Tabs>

## 3. Paginate safely [#3-paginate-safely]

When `meta.has_more` is true, send `meta.next_cursor` back unchanged. Cursors are signed, expire after 24 hours, bind to the original query, and cap a single traversal at 20 pages. Do not parse or construct them.

## Response shape [#response-shape]

```json
{
  "data": [{ "id": "755…", "desc": "…", "play_count": 1834000 }],
  "meta": { "count": 1, "total": 241, "has_more": true, "next_cursor": "eyJ…" },
  "request_id": "9f5f1b55-7c04-4a99-84a5-fce7f2fb6ca6"
}
```

Keep `request_id` when contacting support. See [authentication](/docs/authentication), [rate limits](/docs/rate-limits), and the [API reference](/docs/api/reference).
