> ## 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.

# Resolver mídia

> Tutorial — extrair metadados, formatos e id de uma URL pública

`POST /media/resolve` analisa uma URL pública e retorna metadados normalizados para seu app. Requer escopo `media:resolve`.

## Quando usar

* Exibir título, thumbnail e duração antes do download
* Listar formatos disponíveis para o usuário escolher
* Obter `data.id` para usar como `media_id` no download

## Exemplos

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  curl https://api.shappire.tools/v1/media/resolve \
    -H "X-API-Key: $SHAPPIRE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"url":"https://www.tiktok.com/@user/video/7123456789012345678"}'
  ```

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

  export async function resolveMedia(url) {
    const res = await fetch(`${BASE}/media/resolve`, {
      method: 'POST',
      headers: {
        'X-API-Key': API_KEY,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ url }),
    });

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

    const { data } = await res.json();
    return data;
  }
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import os
  import requests

  API_KEY = os.environ["SHAPPIRE_API_KEY"]
  BASE = "https://api.shappire.tools/v1"

  def resolve_media(url: str) -> dict:
      response = requests.post(
          f"{BASE}/media/resolve",
          headers={"X-API-Key": API_KEY},
          json={"url": url},
          timeout=30,
      )
      response.raise_for_status()
      return response.json()["data"]
  ```

  ```go Go theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  package shappire

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

  const baseURL = "https://api.shappire.tools/v1"

  type resolveResponse struct {
  	Data map[string]interface{} `json:"data"`
  }

  func ResolveMedia(url string) (map[string]interface{}, error) {
  	body, _ := json.Marshal(map[string]string{"url": url})
  	req, err := http.NewRequest(http.MethodPost, baseURL+"/media/resolve", bytes.NewReader(body))
  	if err != nil {
  		return nil, err
  	}
  	req.Header.Set("X-API-Key", os.Getenv("SHAPPIRE_API_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("resolve failed: %s", resp.Status)
  	}

  	var out resolveResponse
  	if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
  		return nil, err
  	}
  	return out.Data, nil
  }
  ```

  ```php PHP theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  <?php

  $apiKey = getenv('SHAPPIRE_API_KEY');
  $base = 'https://api.shappire.tools/v1';

  function resolveMedia(string $url): array {
      global $apiKey, $base;

      $ch = curl_init("$base/media/resolve");
      curl_setopt_array($ch, [
          CURLOPT_POST => true,
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_HTTPHEADER => [
              "X-API-Key: $apiKey",
              'Content-Type: application/json',
          ],
          CURLOPT_POSTFIELDS => json_encode(['url' => $url]),
      ]);

      $body = curl_exec($ch);
      $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);

      if ($status >= 400) {
          throw new RuntimeException("resolve failed ($status): $body");
      }

      return json_decode($body, true)['data'];
  }
  ```

  ```ruby Ruby theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  require 'net/http'
  require 'json'

  API_KEY = ENV.fetch('SHAPPIRE_API_KEY')
  BASE = 'https://api.shappire.tools/v1'

  def resolve_media(url)
    uri = URI("#{BASE}/media/resolve")
    req = Net::HTTP::Post.new(uri)
    req['X-API-Key'] = API_KEY
    req['Content-Type'] = 'application/json'
    req.body = { url: url }.to_json

    res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
    raise res.body unless res.is_a?(Net::HTTPSuccess)

    JSON.parse(res.body)['data']
  end
  ```

  ```java Java theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;

  public class ShappireClient {
    private static final String BASE = "https://api.shappire.tools/v1";
    private final String apiKey;
    private final HttpClient client = HttpClient.newHttpClient();

    public ShappireClient(String apiKey) {
      this.apiKey = apiKey;
    }

    public String resolveMedia(String url) throws Exception {
      String body = String.format("{\"url\":\"%s\"}", url);
      HttpRequest request = HttpRequest.newBuilder()
          .uri(URI.create(BASE + "/media/resolve"))
          .header("X-API-Key", apiKey)
          .header("Content-Type", "application/json")
          .POST(HttpRequest.BodyPublishers.ofString(body))
          .build();

      HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
      if (response.statusCode() >= 400) {
        throw new RuntimeException("resolve failed: " + response.body());
      }
      return response.body();
    }
  }
  ```
</CodeGroup>

## Resposta

```json theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
{
  "data": {
    "id": "med_a1b2c3d4",
    "platform": "tiktok",
    "type": "video",
    "title": "Título do vídeo",
    "thumbnail": "https://...",
    "duration": 42,
    "author": {
      "name": "Autor",
      "username": "@user"
    },
    "formats": [
      {
        "id": "video_best",
        "type": "video",
        "quality": "best",
        "container": "mp4"
      },
      {
        "id": "audio_best",
        "type": "audio",
        "quality": "best",
        "container": "mp3"
      }
    ]
  }
}
```

<Info>
  O campo `data.id` é o identificador público (`med_...`). Envie como `media_id` em `POST /media/download`.
</Info>

## Erros comuns

| Código                 | Causa                               |
| ---------------------- | ----------------------------------- |
| `invalid_url`          | URL malformada ou não HTTP(S)       |
| `unsupported_platform` | Host não suportado (inclui YouTube) |
| `media_unavailable`    | Conteúdo removido ou restrito       |
| `forbidden`            | Chave sem escopo `media:resolve`    |

## TTL do resolve

O `data.id` expira após **1 hora** por padrão (`API_MEDIA_RESOLVE_TTL_SECONDS=3600`). Se receber `media_not_found` no download, execute resolve novamente.

Próximo passo: [Download assíncrono](/pages/guides/download-assincrono)
