EN
Copied
API

API Jobs

Surface unifiée pour toutes les charges exécutées par Outsend — acquisition de sources, enrichissement, vérification, reporting.

API Jobs

L'API Jobs est la surface unifiée pour toutes les charges qu'Outsend exécute pour un tenant : acquisition de sources (scrap) et les modules d'enrichissement, vérification et reporting qui opèrent sur les items résultants. Un job est la seule unité facturable.

Voir aussi :

Tous les endpoints requièrent un cookie de session authentifié. Les endpoints qui créent ou mutent des jobs requièrent en plus un utilisateur actif ; POST /api/jobs et POST /api/jobs/resume exigent aussi un email vérifié. Les routes admin (/api/admin/*, /api/jobs/queue) ne sont pas documentées ici.

Conventions

Élément Valeur
URL de base https://outsend.xyz
Auth Cookie de session (outsend_session)
Content-Type application/json pour les corps POST
Identifiant de job Chaîne opaque (job.id), stable durant la vie du job
Horodatages ISO 8601 UTC

L'objet JobPublic

Tout endpoint qui retourne un job retourne la même forme :

{
  "id": "j_01HXYZ...",
  "job_type": "scrap",
  "queries": ["dentiste"],
  "zones": ["Paris", "75015"],
  "include_reviews": false,
  "status": "running",
  "grid_points_count": 412,
  "processed_points": 87,
  "results_count": 64,
  "error_count": 0,
  "ef_cost": 0.041,
  "created_at": "2026-05-27T09:12:03Z",
  "started_at": "2026-05-27T09:12:05Z",
  "completed_at": null,
  "expires_at": "2026-06-26T09:12:03Z",
  "error_message": null,
  "output_filename": null,
  "download_available": false,
  "source_job_id": null,
  "email_mode": null,
  "breakdown": { "by_query": {"dentiste": 64}, "by_zone": {"Paris": 64} },
  "dead_queries": [],
  "flagged_tiles_count": 0,
  "total_attempts_count": 87,
  "query_stats": { "dentiste": { "tiles": 87, "with_results": 71 } }
}

status prend l'une des valeurs pending | running | done | failed | cancelled | expired.

Erreurs

Tous les endpoints renvoient {"detail": "..."} (ou {"detail": {"message": ..., "errors": [...]}} pour les erreurs de validation). Codes génériques : 401 non authentifié, 403 non autorisé (autre tenant ou email non vérifié), 404 non trouvé, 422 validation Pydantic. Les causes spécifiques sont listées en ligne.


Créer un job (générique)

POST /api/jobs

Crée un job scrap — la charge canonique d'acquisition qui exécute des requêtes sur une grille géographique. Pour toute autre charge, utiliser le raccourci typé décrit plus bas ; passer un champ type à POST /api/jobs n'est pas supporté.

Corps de requête

{
  "queries": ["dentiste", "orthodontiste"],
  "zones": ["Paris", "75015", "Lyon 2e"],
  "include_reviews": false
}
Champ Type Notes
queries string[] (1..20) Chaque item ≤ 200 caractères, trimé, dédupliqué
zones string[] (1..50) Noms de villes, codes postaux ou arrondissements ; résolus côté serveur
include_reviews boolean Si true, récupère les derniers avis par POI (augmente le coût EF)

Réponse200 OK, un JobPublic en statut pending.

Causes spécifiques : 400 parsing de zone échoué / quota EF dépassé / grille vide ; 403 email non vérifié.


Créer un job (raccourci typé)

Chaque module d'enrichissement, vérification et reporting a un endpoint dédié qui accepte les items sur lesquels il opère. Chaque raccourci renvoie un JobPublic dont le job_type est fixé au slug du module.

POST /api/jobs/{type}
type Rôle Doc module
reviews Récupérer les derniers avis par POI reviews
emails Découvrir les emails de contact depuis chaque site emails
verify-emails Vérification anti-bounce (sans VPN) verify-emails
socials Détecter les profils sociaux liés socials
phones-extra Trouver des numéros au-delà du listing Maps phones-extra
legal-ids Extraire SIRET / SIREN depuis le site legal-ids
legal-mentions Parser la page mentions légales (capital, RCS, …) legal-mentions
legal-data Enrichir via SIRENE / INPI (api.gouv.fr) legal-data
pricing Extraire les tarifs SaaS / B2B pricing
techstack Détecter CMS, frameworks, analytics, paiement, CRM techstack
pagespeed Score via Google PSI API v5 pagespeed
ads-intelligence Profilage marketing/ads (pixels, CMP, retargeting) ads-intelligence
brand-assets Logo, favicon, palette, screenshot optionnel brand-assets
dead-check Marquer les sites morts (DNS, parking, default-server, SSL) dead-check
delivery-check Test de placement Gmail Inbox / Promotions / Spam delivery-check

Corps de requête (forme partagée par tous les modules par item)

{
  "items": [
    { "nom": "Cabinet Dupont", "site_web": "https://dupont-dentiste.fr", "ville": "Paris" }
  ],
  "source_job_id": "j_01HXYZ..."
}
Champ Type Notes
items dict[] (1..10 000) Clés spécifiques au module ; généralement un sous-ensemble du CSV d'un job précédent
source_job_id string? Chaîne le nouveau job à un précédent, utilisé pour traçabilité et affichage facturation

Surcharges spécifiques aux modules

json { "domain": "example.com", "subject_filter": "outsend" }

Réponse200 OK, un JobPublic en statut pending. Cause additionnelle : 422 si items est vide, trop grand ou si des clés requises par le module manquent.


Lister les jobs

GET /api/jobs?limit={n}&offset={n}

Renvoie les jobs de l'utilisateur authentifié, les plus récents d'abord.

Param Type Défaut Plage
limit int 100 borné à [1, 500]
offset int 0 ≥ 0

Réponse200 OK, JobPublic[].


Récupérer un job

GET /api/jobs/{id}

Réponse200 OK, un seul JobPublic. Inclut des compteurs live (processed_points, results_count, query_stats, breakdown) que le tableau de bord interroge entre les événements SSE.


Suivre la progression en direct (SSE)

GET /api/jobs/{id}/stream?since={log_id}

Flux Server-Sent Events qui émet les transitions de statut, lignes de log et mises à jour de compteurs au fur et à mesure de l'avancement. Les reconnexions honorent automatiquement Last-Event-ID ; le paramètre since est un fallback pour clients qui ne parlent pas SSE nativement. La taxonomie d'événements (status, log, progress, done, error) et les payloads sont documentés dans États et événements.

En-têtes renvoyés

Content-Type: text/event-stream
Cache-Control: no-cache
X-Accel-Buffering: no

Lister les items d'un job

GET /api/jobs/{id}/items?offset={n}&limit={n}

Renvoie les lignes du CSV de sortie en JSON, pour chaînage vers un job d'enrichissement. Disponible uniquement pour les jobs dont status == "done" et dont le job_type produit un CSV réutilisable (c.-à-d. ni delivery_check ni viewport_test).

Réponse200 OK

{
  "count": 412,
  "items": [
    { "nom": "Cabinet Dupont", "site_web": "https://...", "telephone": "+33 1 ...", "...": "..." }
  ]
}

Causes spécifiques : 400 job non terminé ou job_type sans sortie réutilisable ; 410 CSV expiré ou supprimé.


Télécharger le résultat d'un job

GET /api/jobs/{id}/download?format=csv|json|xlsx

Télécharge la sortie du job. Le CSV est l'artefact canonique écrit par le worker (UTF-8 BOM, séparateur ;) ; JSON et XLSX sont dérivés à la volée. Tous les exports passent par un sanitiseur d'injection de formules tableur.

format Media type Nom de fichier
csv (défaut) text/csv; charset=utf-8 {job.output_filename}
json application/json; charset=utf-8 {base}.json
xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet {base}.xlsx

Causes spécifiques : 400 job encore pending/running ou format non supporté ; 410 sortie expirée, manquante, ou job échoué avant la première ligne.


Arrêter un job

POST /api/jobs/{id}/cancel

Arrête un job pending ou running. Les résultats déjà extraits sont conservés (téléchargeables en CSV partiel et réutilisables). Renvoie 400 si le job est déjà terminal.

Si le job appartient à un pipeline, l'arrêt met la pipeline en pause sur cette étape — les étapes suivantes ne sont pas lancées automatiquement (idem en cas de crash). Pour poursuivre la chaîne avec les résultats partiels, l'utilisateur déclenche explicitement POST /api/pipelines/{id}/nodes/{node_id}/continue (bouton « Continuer avec les résultats »). Pour arrêter au contraire la pipeline entière, utiliser POST /api/pipelines/{id}/cancel.

Réponse200 OK, {"ok": true}.


Reprendre un job

POST /api/jobs/{id}/resume

Crée un nouveau job qui reprend un scrap cancelled ou failed là où il s'est arrêté. Le nouveau job hérite des queries, zones et CSV partiel de la source ; le worker saute les coordonnées déjà traitées. EF est débité uniquement pour les points restants.

Réponse200 OK, un nouveau JobPublic (le job de reprise) en statut pending. Son source_job_id référence l'original.

Causes spécifiques : 400 job source non reprisable (mauvais type, non interrompu, ou déjà entièrement traité) ; 403 email non vérifié.


Supprimer un job

DELETE /api/jobs/{id}

Supprime définitivement le job et son CSV. Refuse de supprimer un job encore en cours — il faut l'arrêter d'abord.

Réponse204 No Content. Cause spécifique : 400 job encore en cours.


Estimer le coût EF

POST /api/estimate

Calcule le coût EF d'un job scrap hypothétique sans le créer. Alimente le compteur de coût live du formulaire de lancement. L'estimation est gratuite et non comptée.

Corps de requête — même forme que POST /api/jobs, mais queries et zones peuvent être vides (renvoie valid: false).

Réponse200 OK, un JobEstimateResponse :

{
  "valid": true,
  "grid_points": 412,
  "total_requests": 824,
  "queries_count": 2,
  "ef_cost": 0.041,
  "estimated_duration_seconds": 1380,
  "errors": [],
  "warnings": []
}
Champ Signification
valid true ssi errors est vide
grid_points Tuiles GPS distinctes sur l'union des zones
total_requests grid_points × len(queries) — ce que le worker appellera réellement
queries_count Reflète len(queries) pour l'affichage UI
ef_cost Unités équivalent France ; voir Limites
estimated_duration_seconds Estimation au mieux du temps horloge
errors Bloqueurs durs (hors-quota, zones impossibles à parser, grille vide)
warnings Signaux doux (non utilisés actuellement)

Notes sur les endpoints omis

Les routes suivantes existent mais ne font pas partie de la surface publique :