Defina
SHAPPIRE_API_KEY no ambiente. Nunca exponha a chave no frontend.Listar plataformas
curl https://api.shappire.tools/v1/platforms \
-H "X-API-Key: $SHAPPIRE_API_KEY"
const res = await fetch('https://api.shappire.tools/v1/platforms', {
headers: { 'X-API-Key': process.env.SHAPPIRE_API_KEY },
});
const { data } = await res.json();
import os, requests
r = requests.get(
"https://api.shappire.tools/v1/platforms",
headers={"X-API-Key": os.environ["SHAPPIRE_API_KEY"]},
)
platforms = r.json()["data"]
req, _ := http.NewRequest("GET", baseURL+"/platforms", nil)
req.Header.Set("X-API-Key", os.Getenv("SHAPPIRE_API_KEY"))
Resolver mídia
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"}'
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;
}
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"]
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
$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'];
}
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
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();
}
}
Enfileirar download
curl -X POST https://api.shappire.tools/v1/media/download \
-H "X-API-Key: $SHAPPIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"media_id":"med_abc123","format":"video_best"}'
const API_KEY = process.env.SHAPPIRE_API_KEY;
const BASE = 'https://api.shappire.tools/v1';
export async function enqueueDownload(mediaId, format) {
const res = await fetch(`${BASE}/media/download`, {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ media_id: mediaId, format }),
});
if (res.status !== 202) {
const err = await res.json();
throw new Error(err.error?.message ?? res.statusText);
}
const { data } = await res.json();
return data.job_id;
}
import os
import requests
API_KEY = os.environ["SHAPPIRE_API_KEY"]
BASE = "https://api.shappire.tools/v1"
def enqueue_download(media_id: str, format: str) -> str:
response = requests.post(
f"{BASE}/media/download",
headers={"X-API-Key": API_KEY},
json={"media_id": media_id, "format": format},
timeout=30,
)
response.raise_for_status()
return response.json()["data"]["job_id"]
func EnqueueDownload(mediaID, format string) (string, error) {
body, _ := json.Marshal(map[string]string{
"media_id": mediaID,
"format": format,
})
req, err := http.NewRequest(http.MethodPost, baseURL+"/media/download", bytes.NewReader(body))
if err != nil {
return "", 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 "", err
}
defer resp.Body.Close()
var out struct {
Data struct {
JobID string `json:"job_id"`
} `json:"data"`
}
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return "", err
}
return out.Data.JobID, nil
}
function enqueueDownload(string $mediaId, string $format): string {
global $apiKey, $base;
$ch = curl_init("$base/media/download");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-API-Key: $apiKey",
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'media_id' => $mediaId,
'format' => $format,
]),
]);
$body = curl_exec($ch);
curl_close($ch);
return json_decode($body, true)['data']['job_id'];
}
def enqueue_download(media_id, format)
uri = URI("#{BASE}/media/download")
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = API_KEY
req['Content-Type'] = 'application/json'
req.body = { media_id: media_id, format: format }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
JSON.parse(res.body)['data']['job_id']
end
public String enqueueDownload(String mediaId, String format) throws Exception {
String body = String.format(
"{\"media_id\":\"%s\",\"format\":\"%s\"}", mediaId, format);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/media/download"))
.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());
return response.body();
}
Consultar job (polling)
curl https://api.shappire.tools/v1/jobs/job_xyz789 \
-H "X-API-Key: $SHAPPIRE_API_KEY"
const API_KEY = process.env.SHAPPIRE_API_KEY;
const BASE = 'https://api.shappire.tools/v1';
export async function waitForJob(jobId, { intervalMs = 3000, timeoutMs = 300000 } = {}) {
const started = Date.now();
while (Date.now() - started < timeoutMs) {
const res = await fetch(`${BASE}/jobs/${jobId}`, {
headers: { 'X-API-Key': API_KEY },
});
const { data } = await res.json();
if (data.status === 'completed') return data;
if (data.status === 'failed') throw new Error(data.error?.message ?? 'job failed');
await new Promise((r) => setTimeout(r, intervalMs));
}
throw new Error('job timeout');
}
import time
def wait_for_job(job_id: str, interval: float = 3.0, timeout: float = 300.0) -> dict:
started = time.time()
while time.time() - started < timeout:
response = requests.get(
f"{BASE}/jobs/{job_id}",
headers={"X-API-Key": API_KEY},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
if data["status"] == "completed":
return data
if data["status"] == "failed":
raise RuntimeError(data.get("error", {}).get("message", "job failed"))
time.sleep(interval)
raise TimeoutError("job timeout")
func WaitForJob(jobID string) (map[string]interface{}, error) {
for {
req, _ := http.NewRequest(http.MethodGet, baseURL+"/jobs/"+jobID, nil)
req.Header.Set("X-API-Key", os.Getenv("SHAPPIRE_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var out struct {
Data map[string]interface{} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&out)
status, _ := out.Data["status"].(string)
switch status {
case "completed":
return out.Data, nil
case "failed":
return nil, fmt.Errorf("job failed")
}
time.Sleep(3 * time.Second)
}
}
function waitForJob(string $jobId, int $timeoutSeconds = 300): array {
global $apiKey, $base;
$started = time();
while (time() - $started < $timeoutSeconds) {
$ch = curl_init("$base/jobs/$jobId");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $apiKey"],
]);
$body = curl_exec($ch);
curl_close($ch);
$data = json_decode($body, true)['data'];
if ($data['status'] === 'completed') return $data;
if ($data['status'] === 'failed') throw new RuntimeException('job failed');
sleep(3);
}
throw new RuntimeException('job timeout');
}
def wait_for_job(job_id, interval: 3, timeout: 300)
started = Time.now
loop do
uri = URI("#{BASE}/jobs/#{job_id}")
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = API_KEY
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)['data']
return data if data['status'] == 'completed'
raise 'job failed' if data['status'] == 'failed'
raise 'job timeout' if Time.now - started > timeout
sleep interval
end
end
public String getJob(String jobId) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/jobs/" + jobId))
.header("X-API-Key", apiKey)
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
return response.body();
}
Fluxo completo
// Requer SHAPPIRE_API_KEY em process.env
import { resolveMedia, enqueueDownload, waitForJob } from './shappire.js';
const url = 'https://www.tiktok.com/@user/video/7123456789012345678';
const media = await resolveMedia(url);
const format = media.formats.find((f) => f.id === 'video_best')?.id ?? 'video_best';
const jobId = await enqueueDownload(media.id, format);
const job = await waitForJob(jobId);
console.log(job.result.download_url);
import os
# Requer SHAPPIRE_API_KEY em os.environ
url = "https://www.tiktok.com/@user/video/7123456789012345678"
media = resolve_media(url)
job_id = enqueue_download(media["id"], "video_best")
job = wait_for_job(job_id)
print(job["result"]["download_url"])
media, err := ResolveMedia("https://www.tiktok.com/@user/video/7123456789012345678")
if err != nil {
log.Fatal(err)
}
mediaID, _ := media["id"].(string)
jobID, err := EnqueueDownload(mediaID, "video_best")
if err != nil {
log.Fatal(err)
}
job, err := WaitForJob(jobID)
if err != nil {
log.Fatal(err)
}
# 1. Resolver
MEDIA=$(curl -s 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"}')
MEDIA_ID=$(echo "$MEDIA" | jq -r '.data.id')
# 2. Enfileirar
JOB=$(curl -s -X POST https://api.shappire.tools/v1/media/download \
-H "X-API-Key: $SHAPPIRE_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"media_id\":\"$MEDIA_ID\",\"format\":\"video_best\"}")
JOB_ID=$(echo "$JOB" | jq -r '.data.job_id')
# 3. Polling
curl https://api.shappire.tools/v1/jobs/$JOB_ID \
-H "X-API-Key: $SHAPPIRE_API_KEY"
Guias por linguagem
JavaScript / Node.js
fetch, axios e worker
Python
requests e asyncio
Go
net/http e context
PHP
curl e Laravel
Ruby
Net::HTTP e Sidekiq
Java
HttpClient e Spring