Questa documentazione descrive l'API REST pubblica di Afflyt per la generazione di link tracciati e il monitoraggio delle quote di utilizzo.
Attenzione
L'API pubblica richiede un piano BETA_TESTER o BUSINESS. I piani FREE e PRO non includono l'accesso API.
Informazioni Base
| Parametro | Valore |
|---|---|
| Base URL | https://api.afflyt.io/public/v1 |
| Autenticazione | Header X-API-Key |
| Formato | JSON |
| Encoding | UTF-8 |
Autenticazione
Tutte le richieste richiedono un header X-API-Key con una chiave API valida.
Generare una API Key
- Accedi alla sezione Developer nelle impostazioni
- Clicca "Genera nuova chiave"
- Assegna un nome identificativo (es. "Bot Telegram Offerte")
- Copia la chiave — viene mostrata una sola volta
Formato della Chiave
afflyt_sk_<43-caratteri-base64url>
Esempio: afflyt_sk_aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789_-abc
Sicurezza
- La chiave viene hashata (SHA-256) prima di essere salvata
- Afflyt non memorizza la chiave in chiaro
- Se perdi la chiave, devi generarne una nuova
- Puoi avere massimo 5 chiavi attive per account
- Revoca le chiavi non utilizzate dalla dashboard
Info
Non condividere mai la tua API key. Trattala come una password. Se sospetti che sia stata compromessa, revocala immediatamente.
Endpoint
POST /links
Genera un link tracciato per un prodotto Amazon.
Request
curl -X POST https://api.afflyt.io/public/v1/links \
-H "Content-Type: application/json" \
-H "X-API-Key: afflyt_sk_your_key_here" \
-d '{
"asin": "B09V3KXJPB",
"amazonTag": "tuotag-21",
"utm_medium": "telegram",
"utm_campaign": "offerte-tech",
"utm_content": "post-123",
"channel_id": "canale-principale"
}'
Parametri Body
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
asin | string | Sì | ASIN Amazon (10 caratteri alfanumerici) |
amazonTag | string | Sì | Il tuo tag Amazon Associates |
utm_medium | string | No | Canale di acquisizione (default: api) |
utm_campaign | string | No | Nome campagna per tracking |
utm_content | string | No | Identificatore contenuto specifico |
channel_id | string | No | ID del canale per analytics cross-channel |
Attenzione
Il prodotto con l'ASIN specificato deve esistere nel database Afflyt. Se il prodotto non esiste, riceverai un errore 404.
Response (201 Created)
{
"id": "clx1abc123def456",
"shortUrl": "https://afflyt.io/r/aBc1234?utm_source=afflyt&utm_medium=telegram&utm_campaign=offerte-tech&utm_content=post-123&ch=canale-principale&p=1705312800",
"amazonUrl": "https://www.amazon.it/dp/B09V3KXJPB?tag=tuotag-21",
"asin": "B09V3KXJPB",
"createdAt": "2025-01-15T12:00:00.000Z",
"quota": {
"used": 42,
"limit": 1000,
"resetDate": "2025-02-01"
}
}
| Campo | Descrizione |
|---|---|
id | ID univoco del link nel database |
shortUrl | URL breve da pubblicare (con tracking UTM) |
amazonUrl | URL Amazon diretto (senza UTM) |
asin | ASIN del prodotto |
createdAt | Timestamp di creazione |
quota.used | Link generati questo mese |
quota.limit | Limite mensile del piano |
quota.resetDate | Data reset quota (primo del mese successivo) |
GET /usage
Restituisce l'utilizzo corrente delle quote mensili.
Request
curl -X GET https://api.afflyt.io/public/v1/usage \
-H "X-API-Key: afflyt_sk_your_key_here"
Response (200 OK)
{
"period": "monthly",
"resetDate": "2025-02-01",
"links": {
"used": 42,
"limit": 1000,
"remaining": 958
},
"clicks": {
"used": 1523,
"limit": 10000,
"remaining": 8477
}
}
Rate Limits
L'API applica due tipi di limiti:
1. Rate Limit (richieste/minuto)
| Piano | Limite |
|---|---|
| BETA_TESTER | 60 req/min |
| BUSINESS | 300 req/min |
Quando superi il rate limit, ricevi un errore 429 Too Many Requests con l'header Retry-After.
2. Quote Mensili
| Piano | Link/mese | Click tracciati/mese |
|---|---|---|
| BETA_TESTER | 1.000 | 10.000 |
| BUSINESS | 10.000 | 100.000 |
Info
Soft Limit vs Hard Limit: I piani BETA_TESTER hanno un soft limit — ricevi un warning nella response ma la richiesta viene comunque processata. I piani BUSINESS hanno un hard limit — la richiesta viene rifiutata con errore 429.
Response con Warning (BETA_TESTER)
Quando superi il soft limit, la response include un campo warnings:
{
"id": "clx1abc123def456",
"shortUrl": "https://afflyt.io/r/aBc1234?...",
"warnings": [
{
"type": "quota_exceeded",
"resource": "links",
"used": 1050,
"limit": 1000
}
]
}
Gestione Errori
Tutti gli errori seguono un formato consistente:
{
"error": "ERROR_CODE",
"message": "Descrizione leggibile dell'errore",
"details": [...]
}
Codici di Errore
| HTTP | Codice | Descrizione | Azione |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Body malformato o parametri invalidi | Controlla il formato dei parametri |
| 401 | — | API key mancante o invalida | Verifica l'header X-API-Key |
| 403 | UPGRADE_REQUIRED | Piano non include API access | Upgrade a BETA_TESTER o BUSINESS |
| 404 | PRODUCT_NOT_FOUND | ASIN non presente nel database | Verifica l'ASIN |
| 429 | QUOTA_EXCEEDED | Quota mensile esaurita | Attendi il reset o fai upgrade |
| 500 | INTERNAL_ERROR | Errore server | Riprova più tardi |
Esempio: Errore di Validazione
{
"error": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{
"field": "asin",
"message": "Invalid ASIN format (must be 10 alphanumeric characters)"
},
{
"field": "amazonTag",
"message": "Amazon tag is required"
}
]
}
Esempio: Quota Esaurita
{
"error": "QUOTA_EXCEEDED",
"message": "Monthly link quota exceeded (1000/1000)",
"resetDate": "2025-02-01",
"upgradeUrl": "/settings/subscription"
}
Esempio: Piano Non Supportato
{
"error": "UPGRADE_REQUIRED",
"message": "API Access is available from BUSINESS plan",
"currentPlan": "PRO",
"currentPlanLabel": "Pro",
"requiredFeature": "api_access",
"suggestedPlan": "BUSINESS",
"suggestedPlanLabel": "Business",
"suggestedPlanPrice": 199,
"upgradeUrl": "/settings/subscription"
}
Esempi di Integrazione
Python
import requests
API_KEY = "afflyt_sk_your_key_here"
BASE_URL = "https://api.afflyt.io/public/v1"
def generate_link(asin: str, amazon_tag: str, channel_id: str = None) -> dict:
"""Genera un link tracciato per un prodotto Amazon."""
response = requests.post(
f"{BASE_URL}/links",
headers={
"Content-Type": "application/json",
"X-API-Key": API_KEY
},
json={
"asin": asin,
"amazonTag": amazon_tag,
"utm_medium": "telegram",
"channel_id": channel_id
}
)
if response.status_code == 201:
return response.json()
elif response.status_code == 429:
raise Exception(f"Quota esaurita: {response.json()['message']}")
else:
raise Exception(f"Errore API: {response.text}")
# Utilizzo
link = generate_link("B09V3KXJPB", "tuotag-21", "canale-tech")
print(f"Short URL: {link['shortUrl']}")
print(f"Quota: {link['quota']['used']}/{link['quota']['limit']}")
Node.js
const API_KEY = 'afflyt_sk_your_key_here';
const BASE_URL = 'https://api.afflyt.io/public/v1';
async function generateLink(asin, amazonTag, options = {}) {
const response = await fetch(`${BASE_URL}/links`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
asin,
amazonTag,
utm_medium: options.utmMedium || 'api',
utm_campaign: options.utmCampaign,
utm_content: options.utmContent,
channel_id: options.channelId
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`API Error: ${error.message}`);
}
return response.json();
}
// Utilizzo
const link = await generateLink('B09V3KXJPB', 'tuotag-21', {
utmMedium: 'telegram',
channelId: 'canale-tech'
});
console.log(`Short URL: ${link.shortUrl}`);
Bot Telegram (Python + python-telegram-bot)
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
import requests
API_KEY = "afflyt_sk_your_key_here"
AMAZON_TAG = "tuotag-21"
async def genera_link(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handler per /link <ASIN>"""
if not context.args:
await update.message.reply_text("Uso: /link <ASIN>")
return
asin = context.args[0].upper()
response = requests.post(
"https://api.afflyt.io/public/v1/links",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={
"asin": asin,
"amazonTag": AMAZON_TAG,
"utm_medium": "telegram",
"channel_id": str(update.effective_chat.id)
}
)
if response.status_code == 201:
data = response.json()
await update.message.reply_text(
f"Link generato:\n{data['shortUrl']}\n\n"
f"Quota: {data['quota']['used']}/{data['quota']['limit']}"
)
elif response.status_code == 404:
await update.message.reply_text("Prodotto non trovato nel database.")
else:
await update.message.reply_text(f"Errore: {response.json().get('message')}")
# Setup bot
app = Application.builder().token("BOT_TOKEN").build()
app.add_handler(CommandHandler("link", genera_link))
app.run_polling()
Best Practices
1. Gestisci gli Errori
Non assumere che ogni richiesta abbia successo. Implementa retry con backoff esponenziale per errori 5xx.
import time
def api_request_with_retry(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt == max_retries - 1:
raise
wait_time = 2 ** attempt # 1, 2, 4 secondi
time.sleep(wait_time)
2. Monitora le Quote
Controlla il campo quota in ogni response. Implementa alerting quando ti avvicini al limite.
def check_quota_warning(response_data):
quota = response_data.get('quota', {})
usage_percent = quota['used'] / quota['limit'] * 100
if usage_percent > 80:
send_alert(f"Attenzione: quota API al {usage_percent:.0f}%")
3. Usa channel_id per Analytics
Il parametro channel_id ti permette di segmentare le analytics per canale nella dashboard Afflyt.
# Ogni canale Telegram ha un ID univoco
channel_id = f"telegram_{update.effective_chat.id}"
4. Cache dei Link
Se pubblichi lo stesso prodotto su più canali, considera di cachare il link per evitare richieste duplicate.
from functools import lru_cache
@lru_cache(maxsize=1000)
def get_or_create_link(asin: str) -> str:
# La prima chiamata genera il link, le successive usano la cache
return generate_link(asin, AMAZON_TAG)['shortUrl']
FAQ
Changelog
| Data | Versione | Note |
|---|---|---|
| 2025-01-15 | v1.0 | Release iniziale con POST /links e GET /usage |
Supporto
- Documentazione: Questa pagina
- Problemi tecnici: Contatta il supporto
- Feature request: Scrivi a api@afflyt.io