Perceive
POST /v2/perceive rend une URL une seule fois dans un véritable navigateur et
vous renvoie chaque sortie que vous demandez à partir de ce rendu unique : Markdown, HTML nettoyé ou
brut, une capture d'écran, un PDF, l'inventaire des liens et des images, et
des données structurées (métadonnées de la page, JSON-LD, titres, tableaux). Il remplace
toute une pile d'appels séparés — url-to-markdown, url-to-screenshot,
url-to-pdf, plus votre propre scraping — par une seule requête.
Voici l'appel utile le plus simple. Envoyez une URL, récupérez du Markdown propre et les métadonnées structurées de la page :
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"]
}'
La réponse contient une URL de téléchargement pré-signée pour le fichier Markdown et le bloc structuré en ligne :
{
"operation_id": "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7",
"status": "completed",
"url": "https://example.com/pricing",
"url_final": "https://example.com/pricing",
"content_hash": "9f2b8c1a...d4e5",
"render_quality": 0.93,
"cache_hit": false,
"outputs": {
"markdown": {
"url": "https://spaces.example.com/...signed...",
"object_key": "env/files/4127/v2-perceive/per_3f9a..._markdown.md",
"size_bytes": 8421,
"content_type": "text/markdown; charset=utf-8",
"expires_in": 900
}
},
"structured": {
"metadata": {
"title": "Pricing",
"description": "Simple, usage-based pricing."
},
"structured_data": [
{"@type": "Product", "name": "Pro", "offers": {"price": "99"}}
]
},
"extraction_tier": "heuristic",
"tokens": {"input": 0, "output": 0},
"cost_cents": 0.0,
"duration_ms": 6230,
"warnings": []
}
À signaler d'emblée.
/v2/perceiveest un endpoint V2 avec son propre compteur de quota. Les forfaits de conversion V1 n'incluent aucune opération perceive V2 — il vous faut un forfait incluant la V2 pour l'appeler. Voir la page de tarification pour les allocations par palier.
Endpoints
| Méthode | Chemin | Objectif |
|---|---|---|
POST |
/v2/perceive |
Perçoit une seule URL et renvoie les sorties que vous avez demandées. |
GET |
/v2/perceive/{operation_id} |
Récupère à nouveau une opération passée avec des URL de téléchargement fraîchement signées. |
POST |
/v2/perceive/batch |
Perçoit jusqu'à 1 000 URL partageant un même jeu d'options. |
GET |
/v2/perceive/batch/{job_id} |
Interroge le statut et les résultats par URL d'un lot. |
Content-Type : application/json sur chaque POST.
Authentification
Authentifiez-vous avec une clé privée dans l'en-tête X-API-Key pour les
appels serveur à serveur. C'est la voie utilisée par les exemples ci-dessous.
X-API-Key: sk_live_your_private_key
Les clés publiques avec un jeton bearer JWT fonctionnent aussi, en utilisant le même flux que
tout autre endpoint — générez un jeton avec votre clé pk_, puis envoyez-le
comme Authorization: Bearer <token>. Le flux complet, y compris le verrouillage
de domaine et le rafraîchissement du jeton, se trouve dans le guide d'authentification.
Chaque clé API porte une liste d'autorisation d'endpoints. Si /v2/perceive
ne figure pas dans la liste de la clé, la requête est rejetée avec un 403.
Fonctionnement de perceive
Une requête déclenche un seul rendu de navigateur via un singleton Chrome headless partagé, puis matérialise chaque sortie à partir de ce rendu. Vous ne payez jamais deux fois pour la même page dans un seul appel.
- Rendu. La page se charge dans Chrome headless. Les bannières de cookies sont rejetées, la page est défilée pour déclencher le contenu chargé en différé, les en-têtes collants sont gérés, et les images ont le temps de se charger — le même pipeline de capture qui alimente l'endpoint url-to-pdf.
- Matérialisation. À partir du DOM rendu, perceive construit ce que vous avez
listé dans
outputs: Markdown, HTML nettoyé/brut, liens, images, une capture d'écran, un PDF. - Extraction. Si vous avez demandé une sortie
structured, perceive effectue une passe heuristique pour les métadonnées de la page, le JSON-LD, les titres et les tableaux. Si vous envoyez aussi unschemaet que votre forfait porte le palier LLM, un modèle Anthropic Claude Haiku remplit le schéma lorsque la passe heuristique reste insuffisante. - Notation. Un score de qualité de rendu (0.0–1.0) signale les pages qui semblent bloquées par une protection anti-bot ou masquées derrière un mur de connexion, afin que vous puissiez distinguer un vrai rendu d'une page de défi.
Les sorties binaires et texte (Markdown, HTML, captures d'écran, PDF, le JSON
des liens et des images) sont téléversées vers le stockage et renvoyées sous forme d'URL pré-signées
qui expirent après 15 minutes. Le bloc structured est renvoyé en ligne
dans le JSON. Récupérez à nouveau n'importe quelle opération avec GET /v2/perceive/{operation_id}
pour obtenir un nouveau jeu d'URL signées.
Paramètres de requête
Cœur
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
url |
string |
— | La page à percevoir. Doit commencer par http:// ou https://. Maximum 2 048 caractères. Requis. |
outputs |
string[] |
["markdown", "structured"] |
Quelles sorties produire. Voir Sorties. |
extract |
string[] |
[] |
Quels champs structurés extraire lorsque structured figure dans outputs. Voir Extraction structurée. |
schema |
object |
null |
Un schéma JSON décrivant les champs que vous voulez extraire. Déclenche le palier d'extraction LLM sur les forfaits qui l'incluent. |
cache_mode |
string |
"enabled" |
enabled, bypass ou refresh. Voir Mise en cache. |
Sorties
outputs accepte n'importe quelle combinaison de ces noms :
| Sortie | Renvoyée comme | Ce que vous obtenez |
|---|---|---|
markdown |
URL signée | Markdown propre issu du contenu principal, liens et images résolus en URL absolues. |
markdown_fit |
URL signée | Markdown « Fit » — une variante élaguée et plus dense, ajustée pour les fenêtres de contexte des LLM. |
html_cleaned |
URL signée | Le HTML rendu avec scripts, styles et boilerplate retirés. |
html_raw |
URL signée | Le HTML rendu complet exactement tel que le navigateur l'a produit. |
screenshot |
URL signée | Un PNG de la zone d'affichage à la taille de viewport demandée (ou par défaut). |
screenshot_full_page |
URL signée | Un PNG pleine page capturant toute la hauteur de défilement. |
pdf |
URL signée | Un PDF de la page. Accepte l'ensemble de la surface pdf_options (voir ci-dessous). |
links |
URL signée | Un tableau JSON de chaque lien trouvé, avec URL absolues et texte d'ancrage. |
images |
URL signée | Un tableau JSON de chaque image, avec src absolu et texte alt. |
structured |
JSON en ligne | Données structurées extraites de la page (le champ de réponse structured). |
Rendu et attente
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
viewport |
object |
1920 x 1080 |
{"width": <int>, "height": <int>}. Largeur 320–3840, hauteur 240–2160. |
mobile |
boolean |
false |
Rendre à un viewport mobile (390 x 844) sauf si viewport est défini explicitement. |
wait_for |
string |
null |
Attendre après la navigation un sélecteur CSS (".price" ou "css:.price") ou une expression JS ("js:window.dataReady === true"). |
wait_timeout_ms |
integer |
30000 |
Combien de temps wait_for peut attendre, en millisecondes. 0–60 000. Un dépassement se dégrade en avertissement ; la page est capturée telle quelle. |
js_code |
string |
null |
JavaScript à exécuter sur la page après la navigation. Maximum 20 000 caractères. Une erreur devient un avertissement, pas un échec. |
block_resources |
string[] |
[] |
Types de ressources à interrompre avant leur chargement. N'importe lesquels parmi image, media, font, stylesheet, script, xhr, fetch, websocket, manifest, other. Utile pour des rendus plus rapides, en texte seul. |
respect_robots |
boolean |
false |
Lorsque true, une URL interdite par le robots.txt du site est rejetée avec un 403. |
pdf_options |
object |
null |
Format de page, marges, en-têtes, pieds de page, échelle et orientation pour la sortie pdf. Même objet que url-to-pdf. Sans pdf_options, perceive produit une seule page continue, identique octet pour octet à url-to-pdf V1. |
Requêtes authentifiées et personnalisées
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
auth |
object |
null |
HTTP Basic Auth pour la page cible : {"username": "...", "password": "..."}. |
cookies |
array |
null |
Cookies à injecter avant la navigation. Maximum 50. Chacun a besoin de name, value, et soit domain soit url. |
headers |
object |
null |
En-têtes de requête personnalisés. Maximum 20. Noms bloqués : host, content-length, transfer-encoding, connection, upgrade, te, trailer. |
proxy_url (Business+),
geolocation et action_chain sont acceptés par le
schéma de requête mais renvoient 422 aujourd'hui. Ils arriveront dans une version
ultérieure ; les envoyer maintenant vous indique exactement quel réglage n'est pas prêt
au lieu de l'ignorer silencieusement.
Extraction structurée
Lorsque structured figure dans outputs, la liste extract contrôle quels
champs perceive extrait. Ne demandez rien et il revient par défaut à metadata et
structured_data.
Valeur extract |
Champ dans structured |
Statut |
|---|---|---|
metadata |
metadata |
En service |
structured_data |
structured_data (JSON-LD) |
En service |
headings |
headings |
En service |
tables |
tables |
En service |
main_content |
main_content (texte, plafonné à 50 000 caractères) |
En service |
all |
s'étend à chaque champ en service ci-dessus | En service |
prices |
— | Pas encore — renvoie un avertissement, omis |
contacts |
— | Pas encore — renvoie un avertissement, omis |
technologies |
— | Pas encore — renvoie un avertissement, omis |
Soyons clairs : prices, contacts et technologies sont des
noms réservés. Demandez-en un aujourd'hui et cela ne provoque pas d'erreur — il atterrit dans le
tableau warnings et est écarté de structured.
Extraction pilotée par schéma
Envoyez un schema pour extraire des champs spécifiques dans structured.extracted :
{
"url": "https://example.com/product/widget",
"outputs": ["markdown", "structured"],
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"},
"in_stock": {"type": "boolean"}
}
}
}
Le palier d'extraction LLM (Anthropic Claude Haiku) remplit le schéma, et il
ne se déclenche que lorsque toutes ces conditions sont réunies : vous avez envoyé un schema, votre forfait
porte le palier LLM (Starter et au-dessus), la page n'a pas été notée comme
bloquée, et la passe heuristique a laissé les champs du schéma vides. Lorsqu'il s'exécute,
extraction_tier vaut "llm", et tokens et cost_cents rapportent ce que
cette extraction a coûté ; sinon extraction_tier vaut "heuristic" et
les deux sont à zéro.
Note. L'extraction par schéma est strictement plafonnée pour protéger votre facture : une extraction unique est plafonnée par requête, et la dépense quotidienne par projet est plafonnée selon le forfait. Si un plafond est atteint, perceive renvoie le résultat heuristique avec une note dans
warningsplutôt que de dépenser au-delà. Sur un forfait sans le palier LLM, vous n'obtenez que des donnéesstructuredheuristiques.
Réponse
POST /v2/perceive et GET /v2/perceive/{operation_id} renvoient tous deux le
même objet.
| Champ | Type | Description |
|---|---|---|
operation_id |
string |
ID opaque (per_...). Utilisez-le avec l'endpoint GET et citez-le au support. |
status |
string |
queued, processing, completed ou failed. |
url |
string |
L'URL que vous avez envoyée. |
url_final |
string |
L'URL après redirections. |
content_hash |
string |
SHA-256 de la page rendue. Pilote le cache d'une heure. |
render_quality |
number |
0.0–1.0. Les scores faibles signalent des défis anti-bot ou des murs de connexion. |
cache_hit |
boolean |
true lorsque le résultat provient du cache au lieu d'un rendu frais. |
outputs |
object |
Table de correspondance nom de sortie vers {url, object_key, size_bytes, content_type, expires_in}. Les URL signées expirent en 900 secondes. |
structured |
object |
Données structurées en ligne, présentes lorsque structured a été demandé. |
extraction_tier |
string |
heuristic, css ou llm. |
tokens |
object |
{input, output} tokens LLM utilisés. Zéro sauf si le palier LLM s'est exécuté. |
cost_cents |
number |
Coût LLM en cents pour cette opération. Zéro sauf si le palier LLM s'est exécuté. |
duration_ms |
integer |
Temps de rendu de bout en bout. |
error |
string |
Défini uniquement lorsque status est failed. |
warnings |
string[] |
Notes non fatales : un dépassement de wait_for, une extraction ignorée, un drapeau de page bloquée. |
Récupérer une opération
Les URL signées expirent après 15 minutes. Pour télécharger une sortie plus tard, récupérez à nouveau l'opération — perceive re-signe chaque URL à partir des clés d'objet stockées. Aucun nouveau rendu n'a lieu, donc cela ne consomme pas de quota.
curl https://api.enconvert.com/v2/perceive/per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7 \
-H "X-API-Key: sk_live_your_private_key"
Un ID d'opération inconnu, ou un qui appartient à un projet différent,
renvoie un 404 — l'existence n'est jamais divulguée entre projets.
Perception par lot
POST /v2/perceive/batch perçoit une liste d'URL qui partagent un même
bloc options. Chaque URL est rendue par le même pipeline qu'un
appel unique et produit sa propre ligne d'opération.
curl -X POST https://api.enconvert.com/v2/perceive/batch \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/a",
"https://example.com/b",
"https://example.com/c"
],
"options": {"outputs": ["markdown"]},
"output_mode": "manifest"
}'
Les lots de 10 URL ou moins s'exécutent en ligne et répondent 200 avec chaque résultat
renseigné. Les lots plus grands répondent 202 avec un job_id ; les URL sont
drainées une à une et vous interrogez les résultats :
curl https://api.enconvert.com/v2/perceive/batch/{job_id} \
-H "X-API-Key: sk_live_your_private_key"
La réponse de lot rapporte la progression agrégée et porte un résultat perceive complet par URL une fois rendue :
{
"job_id": "bat_8c1a...",
"status": "partial",
"output_mode": "manifest",
"total": 3,
"completed": 2,
"failed": 1,
"pending": 0,
"items": [
{"operation_id": "per_...", "status": "completed", "url": "https://example.com/a"},
{"operation_id": "per_...", "status": "completed", "url": "https://example.com/b"},
{"operation_id": "per_...", "status": "failed", "url": "https://example.com/c"}
]
}
status vaut queued, processing, completed, failed ou partial
(certaines URL ont réussi, d'autres ont échoué). Réglez output_mode sur zip pour regrouper
chaque artefact dans un seul ZIP, renvoyé sous le champ zip une fois que le
lot est terminé. L'accès aux lots et la taille maximale de lot dépendent du forfait ;
voir la page de tarification.
Mise en cache
cache_mode contrôle la façon dont perceive traite son cache de résultats d'une heure, clé
par votre projet, l'URL et les options de requête affectant le rendu.
cache_mode |
Comportement |
|---|---|
enabled (défaut) |
Renvoyer un résultat en cache lorsqu'une requête identique a été rendue au cours de la dernière heure. cache_hit est true, cost_cents est 0. |
bypass |
Ignorer le cache et rendre à neuf. |
refresh |
Rendre à neuf et remplacer l'entrée en cache. |
À signaler : un hit de cache compte tout de même comme une opération perceive sur votre quota mensuel. Le quota mesure les opérations, pas les rendus de navigateur — le cache vous fait gagner du temps de rendu, pas du quota.
Exemples de code
curl — Markdown seul
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/blog/post",
"outputs": ["markdown"]
}'
curl — Markdown plus données structurées
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"],
"extract": ["metadata", "structured_data", "tables"]
}'
curl — Sorties complètes plus PDF
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/report",
"outputs": ["markdown", "html_cleaned", "screenshot_full_page", "pdf"],
"pdf_options": {"format": "A4", "print_background": true}
}'
Python
import requests
response = requests.post(
"https://api.enconvert.com/v2/perceive",
headers={"X-API-Key": "sk_live_your_private_key"},
json={
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"],
"extract": ["metadata", "tables"],
},
)
response.raise_for_status()
data = response.json()
# Download the Markdown artifact from its signed URL
markdown_url = data["outputs"]["markdown"]["url"]
markdown_text = requests.get(markdown_url).text
print(data["structured"])
print(markdown_text)
Node.js
const res = await fetch("https://api.enconvert.com/v2/perceive", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk_live_your_private_key"
},
body: JSON.stringify({
url: "https://example.com/pricing",
outputs: ["markdown", "structured"],
extract: ["metadata", "tables"]
})
});
const data = await res.json();
// Download the Markdown artifact from its signed URL
const markdownText = await fetch(data.outputs.markdown.url).then(r => r.text());
console.log(data.structured);
console.log(markdownText);
Si vous appelez EnConvert depuis Claude, Cursor ou un autre client MCP, la
même capacité est exposée sous forme d'outil perceive_url — voir la page du serveur
MCP.
Restrictions par forfait
Les opérations perceive V2 sont un compteur séparé des conversions V1. Les forfaits V1 n'incluent aucun quota perceive ; il vous faut un forfait incluant la V2.
| Forfait | Opérations perceive / mois | Extraction par schéma (palier LLM) |
|---|---|---|
| Free | 50 | Non inclus |
| Starter | 850 | Claude Haiku |
| Pro | 8,500 | Claude Haiku |
| Business | 42,500 | Claude Haiku |
| Enterprise | Illimité | Inclus |
L'accès aux lots et les limites de taille par lot, la prise en charge de basic-auth/cookie/header,
et proxy_url (Business+, quand il sortira) dépendent du forfait. Les chiffres actuels
se trouvent sur la page de tarification.
Réponses d'erreur
| Statut | Condition |
|---|---|
400 Bad Request |
L'URL n'est pas http(s), porte des identifiants intégrés, ou se résout vers une adresse privée, loopback ou link-local (protection SSRF). |
400 Bad Request |
auth invalide (username/password manquant), cookies (pas un tableau, plus de 50 entrées, champs manquants), ou headers (pas un objet, plus de 20 entrées, nom bloqué). |
401 Unauthorized |
Clé API / jeton JWT manquant ou invalide. |
402 Payment Required |
Perceive ne fait pas partie de votre forfait actuel, ou votre quota perceive mensuel est épuisé. |
403 Forbidden |
/v2/perceive ne figure pas dans les endpoints autorisés de la clé API. |
403 Forbidden |
Lot non disponible sur votre forfait, ou taille de lot dépassant la limite de votre forfait. |
403 Forbidden |
respect_robots=true et le robots.txt du site interdit l'URL. |
404 Not Found |
operation_id ou job_id inconnu, ou appartenant à un autre projet. |
422 Unprocessable Entity |
La validation de la requête a échoué (mauvais enum dans outputs/extract, wait_timeout_ms hors plage, viewport hors limites). |
422 Unprocessable Entity |
proxy_url, geolocation ou action_chain a été envoyé — réservé pour une version ultérieure. |
500 Internal Server Error |
Le rendu a échoué. Le message inclut l'operation_id à citer au support. |
La référence complète des codes de statut se trouve dans le guide des codes d'erreur.
Limites
| Limite | Valeur |
|---|---|
| Longueur d'URL | 2 048 caractères |
wait_timeout_ms |
0–60 000 ms |
Longueur de js_code |
20 000 caractères |
| Largeur de viewport | 320–3 840 px |
| Hauteur de viewport | 240–2 160 px |
| Cookies par requête | 50 |
| En-têtes personnalisés par requête | 20 |
Extraction main_content |
50 000 caractères |
| URL par requête de lot | 1 000 (plafond du schéma ; la limite de lot de votre forfait est plus basse) |
| Seuil de lot en ligne | 10 URL (les lots plus grands s'exécutent de manière asynchrone) |
| TTL du cache de résultats | 1 heure |
| Expiration des URL signées | 15 minutes |
| Opérations perceive mensuelles | Dépendant du forfait (Free : 50) |