API de Scraping Web pour Markdown, Captures d'écran et Données structurées#
POST /v2/perceive est l'API de web scraping d'EnConvert : elle effectue le rendu
d'une URL une seule fois dans un vrai navigateur headless (JavaScript exécuté, contenu
en chargement différé chargé) et renvoie chaque sortie que vous demandez à partir de ce
rendu unique : Markdown propre (par défaut le contenu principal seul, débarrassé de
l'habillage du site), 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). Les sorties fichiers reviennent sous forme d'URL de téléchargement pré-signées
et de courte durée, le bloc structuré en ligne, et les lots de plus de 10 URL s'exécutent
de manière asynchrone via un job_id interrogeable. Une seule requête remplace toute une
pile d'appels séparés : url-to-markdown, url-to-screenshot, url-to-pdf, plus votre propre
scraping.
Voici le plus petit appel utile. Envoyez une URL, récupérez du Markdown propre et les métadonnées structurées de la page en retour :
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_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": "Studio", "offers": {"price": "99"}}
]
},
"extraction_tier": "heuristic",
"tokens": {"input": 0, "output": 0},
"cost_cents": 0.0,
"duration_ms": 6230,
"warnings": []
}
Points de terminaison#
| Méthode | Chemin | Objectif |
|---|---|---|
POST |
/v2/perceive |
Perçoit une seule URL et renvoie les sorties 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. |
DELETE |
/v2/perceive/batch/{job_id} |
Annule un lot en cours d'exécution. |
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 méthode utilisée dans les exemples ci-dessous.
X-API-Key: sk_your_private_key
Les clés publiques avec un token JWT bearer fonctionnent également, en suivant le même
flux que tout autre point de terminaison : générez un token avec votre clé pk_, puis
envoyez-le sous la forme Authorization: Bearer <token>. Le flux complet, y compris le
verrouillage de domaine et le rafraîchissement de token, se trouve dans le guide
d'authentification.
Chaque clé API porte une liste blanche de points de terminaison autorisés. Si
/v2/perceive ne figure pas dans la liste de la clé, la requête est rejetée avec 403.
Comment fonctionne perceive#
Une requête déclenche un 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 au sein d'un seul appel.
- Rendu. La page est récupérée via un repli multi-moteurs automatique : d'abord une empreinte TLS rapide de vrai navigateur, avec escalade vers Chrome headless lorsque la page est bloquée ou nécessite JavaScript, puis une fois de plus vers un rendu renforcé en mode furtif lorsqu'une page semble toujours bloquée par une protection anti-bot, de sorte que davantage de pages du monde réel reviennent avec un contenu exploitable. Dans le navigateur, les bannières de cookies sont fermées, la page est défilée pour déclencher le contenu en chargement différé, les en-têtes collants sont gérés, et les images ont le temps de se charger. C'est le même pipeline de capture qui alimente le point de terminaison url-to-pdf.
- Matérialisation. À partir du DOM rendu, perceive construit tout ce que vous avez
listé dans
outputs: Markdown, HTML nettoyé/brut, liens, images, une capture d'écran, un PDF. Le DOM est d'abord normalisé pour que le Markdown reflète ce que voit un lecteur : les clôtures de code conservent leur langage, les liens de carte leur structure, et le mobilier d'interface est retiré sousonly_main_content. Voir Qualité du Markdown. - Extraction. Si vous avez demandé la 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 également unschemaet que votre plan inclut le palier LLM, une extraction assistée par LLM complète le schéma lorsque la passe heuristique ne suffit pas. - Score. Un score de qualité de rendu (0.0–1.0) distingue un vrai rendu d'un
rendu échoué. Un score inférieur à 0.40 signifie un rendu échoué : une page
anti-bot, un mur de connexion, une page d'erreur HTTP, un soft 404 ou une coquille
vide. Consultez
deductionspour connaître la raison etstatus_codepour le statut du serveur d'origine.
Les sorties binaires et textuelles (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 la requête#
La validation est stricte : une clé de requête inconnue du schéma est rejetée avec
422 en nommant le champ concerné. Les clés inconnues ne sont jamais ignorées
silencieusement. Chaque corps 422 contient en outre un tableau errors de premier
niveau avec des messages lisibles par un humain, à côté de la liste detail lisible
par machine.
Principaux#
| Paramètre | Type | Valeur par défaut | Description |
|---|---|---|---|
url |
string |
-- | La page à percevoir. Doit commencer par http:// ou https://. 2,048 caractères max. Obligatoire. |
outputs |
string[] |
["markdown", "structured"] |
Les sorties à produire. Voir Sorties. |
extract |
string[] |
[] |
Les 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 souhaitez extraire. Déclenche le palier d'extraction LLM sur les plans qui l'incluent. |
only_main_content |
boolean |
true |
Retire l'habillage du site (navigation, en-tête, pied de page, barres latérales, bannières de cookies, nœuds masqués) ainsi que le mobilier d'interface (boutons, barres d'onglets, widgets « Cette page vous a-t-elle été utile ? », libellés réservés aux lecteurs d'écran, fils d'Ariane) de la sortie markdown et de l'extraction main_content, derrière un garde-fou de fidélité : si le nettoyage supprimait trop de contenu réel, la page complète est renvoyée à la place et un avertissement est ajouté. Les URL d'images sont rendues sous forme de leur texte alt (la liste complète des images reste disponible via outputs: ["images"]). Définissez false pour la page complète, sans rien retirer. Voir Qualité du Markdown. |
truncate_data_arrays |
boolean |
non défini | Réduit les longues suites de littéraux numériques (vecteurs d'embeddings bruts, dumps de tenseurs affichés dans les cellules de sortie de notebooks) à un échantillon initial suivi d'un décompte, par ex. ... [truncated 1520 of 1536 values]. Non défini, l'option suit only_main_content : active quand la page est nettoyée, inactive quand vous avez demandé la page telle quelle. Définissez true ou false pour la contrôler explicitement. |
allow_degraded |
boolean |
false |
Renvoie le rendu même lorsqu'il s'agit d'un défi anti-bot ou d'une page de blocage sans contenu de page. Par défaut, un tel rendu échoue avec 502 au lieu de livrer le texte de l'interstitiel comme s'il s'agissait de la page. |
direct_download |
boolean |
false |
Renvoie les octets de l'artefact directement dans le corps de la réponse HTTP au lieu d'une enveloppe JSON. Exige exactement une sortie produisant un artefact. Requêtes à URL unique uniquement, car le point de terminaison de lot le rejette avec 422. Voir Téléchargement direct. |
cache_mode |
string |
"enabled" |
enabled, bypass ou refresh. Voir Mise en cache. |
Sorties#
outputs accepte n'importe quelle combinaison des noms suivants :
| Sortie | Renvoyée sous forme de | Ce que vous obtenez |
|---|---|---|
markdown |
URL signée | Markdown propre de la page. Avec only_main_content (par défaut true), l'habillage du site tel que la navigation, l'en-tête, le pied de page, les barres latérales, les bannières de cookies et les nœuds masqués est retiré derrière un garde-fou de fidélité, et les URL d'images sont rendues sous forme de leur texte alt. Les blocs de code conservent leur langage sur la clôture (```python) dans les deux modes. Définissez only_main_content: false pour la page complète. Voir Qualité du Markdown. |
html_cleaned |
URL signée | Le HTML rendu, débarrassé des scripts, des styles et du code répétitif. |
html_raw |
URL signée | Le HTML rendu complet, tel que produit par le navigateur. |
screenshot |
URL signée | Un PNG de la fenêtre d'affichage à la taille 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 des options pdf_options (voir ci-dessous). |
links |
URL signée | Un tableau JSON de tous les liens trouvés, avec URL absolues et texte d'ancrage. |
images |
URL signée | Un tableau JSON de toutes les images, avec src absolu et texte alt. |
structured |
JSON en ligne | Données structurées extraites de la page (le champ de réponse structured). |
Qualité du Markdown#
Avant la conversion de la page, le DOM rendu est normalisé afin que le Markdown reflète ce que voit un lecteur plutôt que la façon dont la page a été construite. Cela s'exécute à chaque rendu, si bien que le résultat ne dépend pas de la stratégie d'extraction qui l'emporte pour une page donnée.
Toujours appliqué, dans les deux modes de only_main_content :
- Les clôtures de code conservent leur langage. Le langage est lu
depuis la convention utilisée par le site (
class="language-python",data-lang, un attributlanguagenu ou un wrapper de coloration syntaxique) puis normalisé, de sorte que```pythonarrive au lieu d'une clôture nue. - Les liens de carte restent lisibles. Un lien qui enveloppe un
titre et une description devient un titre lié suivi de sa
description, au lieu d'un seul lien agglutiné comme
[DatabaseSupabase provides a full Postgres database...]. L'URL de destination est préservée. - Les titres tiennent sur une seule ligne. Un titre dont le texte
se trouve dans un élément imbriqué n'émet plus un
##nu avec le texte échoué en dessous. - Les éléments adjacents ne se concatènent plus. Les mises en page
qui espacent leurs éléments en CSS plutôt qu'avec des blancs
produisaient
YesNoetEvaluationDeploymentProduction; ils se lisent désormais comme des mots séparés. - Les caractères invisibles sont supprimés : espaces de largeur nulle utilisés comme libellés d'ancre, traits d'union conditionnels et glyphes de la zone à usage privé des polices d'icônes, qui arrivent sous forme de jetons non imprimables.
- Les éléments vides sont écartés : éléments
<i>ne contenant qu'une icône et rendus en__égarés, et liens dont le libellé est vide.
De plus, avec only_main_content: true :
- Les contrôles d'interface sont supprimés : boutons, barres d'onglets, indications de raccourcis clavier, actions « Copy page » / « On this page » et widgets de notation « Cette page vous a-t-elle été utile ? Oui/Non ». Un contrôle porteur de contenu réel (une question de FAQ, le corps d'une carte cliquable) est conservé.
- Le texte réservé aux lecteurs d'écran est supprimé : liens d'évitement et libellés « Section titled ... » que de nombreux thèmes de documentation attachent à chaque titre.
- Le non-contenu déclaré par le site est respecté : blocs marqués
data-nosnippet,data-pagefind-ignoreoudata-noindex, sauf s'ils contiennent des titres ou du code. - Les blocs dupliqués sont fusionnés : les designs responsives qui livrent une copie desktop et une copie mobile de la même barre, et les carrousels qui pré-rendent chaque image, n'apparaissent qu'une fois.
- Les fils d'Ariane et les surtitres placés au-dessus du titre de la page sont écartés.
Le contenu différé est délibérément conservé : un panneau d'onglet inactif à l'intérieur de la région de contenu abrite un véritable exemple de code (l'exemple Python dans un onglet, celui en JavaScript dans un autre), de sorte que les deux atteignent le Markdown, et pas seulement l'onglet qui se trouvait sélectionné au moment du rendu.
Rendu et attente#
| Paramètre | Type | Valeur par défaut | Description |
|---|---|---|---|
viewport |
object |
1920 x 1080 |
{"width": <int>, "height": <int>}. Largeur 320–3840, hauteur 240–2160. |
mobile |
boolean |
false |
Effectue le rendu avec une fenêtre d'affichage mobile (390 x 844), sauf si viewport est défini explicitement. |
wait_for |
string |
null |
Attend 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 |
Durée maximale d'attente pour wait_for, en millisecondes. 0–60,000. Un dépassement de délai devient un avertissement ; la page est capturée telle quelle. |
js_code |
string |
null |
JavaScript à exécuter sur la page après la navigation. 20,000 caractères max. Une erreur devient un avertissement, pas un échec. |
block_resources |
string[] |
[] |
Types de ressources à interrompre avant leur chargement. Parmi image, media, font, stylesheet, script, xhr, fetch, websocket, manifest, other. Utile pour des rendus texte plus rapides. |
respect_robots |
boolean |
false |
Lorsque défini sur true, une URL interdite par le robots.txt du site est rejetée avec 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 pour url-to-pdf. Sans pdf_options, perceive produit une seule page continue, identique octet pour octet à url-to-pdf en V1. |
Requêtes authentifiées et personnalisées#
| Paramètre | Type | Valeur par défaut | Description |
|---|---|---|---|
auth |
object |
null |
Authentification HTTP Basic pour la page cible : {"username": "...", "password": "..."}. |
cookies |
array |
null |
Cookies à injecter avant la navigation. 50 max. Chacun nécessite name, value, et soit domain, soit url. |
headers |
object |
null |
En-têtes de requête personnalisés. 20 max. Noms bloqués : host, content-length, transfer-encoding, connection, upgrade, te, trailer. |
proxy_url (Production+),
geolocation et action_chain sont acceptés par le
schéma de requête, mais renvoient 422 pour le moment. Ils arriveront
dans une prochaine version ; les envoyer dès maintenant vous indique précisément
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 les champs que
perceive extrait. Si vous ne demandez rien, elle utilise par défaut metadata et
structured_data.
Valeur extract |
Champ dans structured |
Statut |
|---|---|---|
metadata |
metadata |
Actif |
structured_data |
structured_data (JSON-LD) |
Actif |
headings |
headings |
Actif |
tables |
tables |
Actif |
main_content |
main_content (texte, plafonné à 50,000 caractères) |
Actif |
all |
s'étend à tous les champs actifs ci-dessus | Actif |
prices |
-- | Pas encore actif : renvoie un avertissement, omis |
contacts |
-- | Pas encore actif : renvoie un avertissement, omis |
technologies |
-- | Pas encore actif : renvoie un avertissement, omis |
Pour être clair : prices, contacts et technologies sont des noms réservés. En
demander un aujourd'hui ne provoque pas d'erreur. Le nom atterrit dans le tableau
warnings et est retiré 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 complète le schéma, et il se
déclenche uniquement lorsque toutes ces conditions sont réunies : vous avez envoyé
un schema, votre plan inclut le palier LLM (Indie et au-dessus), la page n'a pas été
notée comme bloquée, et la passe heuristique a laissé des champs du schéma vides. Quand
il s'exécute, extraction_tier vaut "llm", et tokens ainsi que cost_cents
indiquent ce que cette extraction a coûté ; sinon, extraction_tier vaut "heuristic"
et les deux valent zéro.
Note. L'extraction par schéma est strictement plafonnée pour protéger votre facture : une seule extraction est plafonnée par requête, et la dépense du projet puise dans votre solde mensuel de crédits IA ($5 / $15 / $40 par mois sur Indie / Studio / Production ; les crédits inutilisés sont reportés). L'extraction LLM consomme des crédits, pas des ops. Si un plafond est atteint ou que le solde est épuisé, perceive renvoie le résultat heuristique avec une note dans
warningsplutôt que de dépasser le budget. Sur un plan 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 le point de terminaison 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'1 heure. |
render_quality |
number |
0.0–1.0. Un score inférieur à 0.40 signifie un rendu échoué : une page anti-bot, un mur de connexion, une page d'erreur HTTP, un soft 404 ou une coquille vide. Consultez deductions pour connaître la raison et status_code pour le statut du serveur d'origine. |
status_code |
integer |
Statut HTTP de la réponse finale du document principal (p. ex. 200, 404). null lorsqu'il est inconnu. |
deductions |
object |
Déductions nommées de qualité de rendu qui se sont appliquées, p. ex. {"http_error": 0.7}, {"soft_404": 0.65}, {"login_wall": 0.65}. Vide pour un rendu propre. |
options_echo |
object |
Écho des options de requête que le serveur a appliquées. Les secrets sont réduits à des booléens (auth_provided, cookies_provided, headers_provided, js_code_provided, schema_provided, pdf_options_provided). Les options simples (outputs, only_main_content, truncate_data_arrays, allow_degraded, extract, cache_mode, mobile, respect_robots, direct_download, wait_for, wait_timeout_ms, viewport, block_resources) sont renvoyées telles qu'appliquées. truncate_data_arrays est renvoyée sous forme de booléen résolu, de sorte que même laissée non définie vous savez dans quel sens elle a été tranchée. |
cache_hit |
boolean |
true lorsque le résultat provient du cache plutôt que d'un rendu frais. |
outputs |
object |
Correspondance entre le nom de la sortie et {url, object_key, size_bytes, content_type, expires_in}. Les URL signées expirent au bout de 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 |
Tokens LLM {input, output} utilisés. Zéro sauf si le palier LLM s'est exécuté. |
cost_cents |
number |
Coût LLM en centimes 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 vaut failed. |
warnings |
string[] |
Notes non fatales : un délai wait_for dépassé, une extraction ignorée, un signalement de page bloquée, un repli de only_main_content vers la page complète, une note indiquant que de longs tableaux de données numériques ont été tronqués. |
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, ce qui ne consomme donc aucune op.
curl https://api.enconvert.com/v2/perceive/per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7 \
-H "X-API-Key: sk_your_private_key"
Un ID d'opération inconnu, ou appartenant à un autre projet, renvoie 404.
L'existence n'est jamais divulguée entre projets.
Téléchargement direct#
Par défaut, chaque sortie fichier revient sous forme d'URL pré-signée que vous
récupérez dans une seconde requête. Définissez direct_download: true sur le POST
pour sauter l'enveloppe : le corps de la réponse HTTP est les octets de
l'artefact, sans JSON, sans URL signée et sans second téléchargement. La requête
doit produire exactement une sortie générant un artefact (outputs: ["markdown"],
outputs: ["pdf"], …), sinon elle est rejetée avec 400. Les métadonnées qui
auraient figuré dans le JSON voyagent à la place dans les en-têtes de réponse :
Content-Disposition, X-Operation-Id, X-Object-Key, X-Cache-Hit,
X-Render-Quality, X-Source-Status-Code, X-Content-Hash et X-Warnings-Count.
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/blog/post",
"outputs": ["markdown"],
"direct_download": true
}' \
-o post.md
Les points de terminaison GET diffusent les artefacts stockés de la même manière :
GET /v2/perceive/{operation_id}?direct_download=true&output=markdowndiffuse un artefact d'une opération passée.outputest obligatoire lorsque l'opération a produit plus d'un artefact. Un artefact au-delà de la fenêtre de rétention de votre plan répond410.GET /v2/perceive/batch/{job_id}?direct_download=truediffuse le ZIP du lot pour les lots enoutput_mode: "zip"dont l'archive est prête, et répond400sinon.
direct_download est réservé aux URL uniques : POST /v2/perceive/batch le rejette
avec 422. Définissez output_mode sur "zip" et téléchargez l'archive à la place.
Voir Perception par lot.
Perception par lot#
POST /v2/perceive/batch perçoit une liste d'URL partageant un même bloc options.
Chaque URL est rendue via 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_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 importants répondent 202 avec un job_id ; les URL
sont traitées une à une et vous interrogez les résultats :
curl https://api.enconvert.com/v2/perceive/batch/{job_id} \
-H "X-API-Key: sk_your_private_key"
La réponse du lot rapporte la progression agrégée et porte un résultat perceive complet par URL une fois le rendu effectué :
{
"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, partial (certaines URL
ont réussi, d'autres ont échoué) ou canceled. Définissez output_mode sur zip pour
regrouper chaque artefact dans un seul ZIP, renvoyé sous le champ zip une fois le lot
terminé.
Durabilité et reprise automatique#
Les lots résistent aux redémarrages. Si le service redémarre alors qu'un lot est en cours, le lot reprend automatiquement et n'effectue un nouveau rendu que pour les URL qui n'étaient pas terminées, donc les URL déjà terminées conservent leurs artefacts. Vous n'avez jamais besoin de soumettre à nouveau un lot à cause d'un redémarrage.
Annuler un lot#
DELETE /v2/perceive/batch/{job_id} annule un lot en cours d'exécution. Le worker
s'arrête entre deux URL, de sorte que les URL déjà rendues conservent leurs résultats
et que les autres ne sont pas démarrées. L'appel est idempotent, donc annuler un lot
déjà terminé renvoie simplement son état actuel, et le status du lot devient
canceled.
curl -X DELETE https://api.enconvert.com/v2/perceive/batch/{job_id} \
-H "X-API-Key: sk_your_private_key"
Mise en cache#
cache_mode contrôle la manière dont perceive traite son cache de résultats d'1
heure, indexé par votre projet, l'URL, et les options de requête affectant le rendu.
cache_mode |
Comportement |
|---|---|
enabled (par défaut) |
Renvoie un résultat mis en cache lorsqu'une requête identique a été rendue au cours de la dernière heure. cache_hit vaut true, cost_cents vaut 0. |
bypass |
Ignore le cache et effectue un rendu frais. |
refresh |
Effectue un rendu frais et remplace l'entrée en cache. |
À noter : un cache hit facture tout de même une op sur votre quota mensuel d'ops. Le quota mesure les opérations plutôt que les rendus de navigateur, donc le cache vous fait gagner du temps de rendu, pas des ops.
Exemples de code#
curl : Markdown seul#
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/blog/post",
"outputs": ["markdown"]
}'
curl : Markdown et données structurées#
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"],
"extract": ["metadata", "structured_data", "tables"]
}'
curl : Toutes les sorties et PDF#
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_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_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_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
fonctionnalité est exposée sous forme d'outil perceive_url. Voir la page du serveur
MCP.
Réponses d'erreur#
| Statut | Condition |
|---|---|
400 Bad Request |
L'URL n'est pas en http(s), contient 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 invalide (pas un tableau, plus de 50 entrées, champs manquants), ou headers invalide (pas un objet, plus de 20 entrées, nom bloqué). |
401 Unauthorized |
Clé API / token JWT manquant ou invalide. |
402 Payment Required |
Perceive n'est pas inclus dans votre plan actuel, ou votre quota mensuel d'ops est épuisé. |
403 Forbidden |
/v2/perceive ne figure pas dans les points de terminaison autorisés de la clé API. |
403 Forbidden |
Le traitement par lot n'est pas disponible sur votre plan, ou la taille du lot dépasse la limite de votre plan. |
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 |
Échec de la validation de la requête (valeur d'énumération invalide dans outputs/extract, wait_timeout_ms hors limites, viewport hors limites, une clé de requête inconnue). |
422 Unprocessable Entity |
proxy_url, geolocation ou action_chain a été envoyé. Ces trois options sont réservées pour une version ultérieure. |
500 Internal Server Error |
Le rendu a échoué. Le message inclut l'operation_id à citer au support. |
502 Bad Gateway |
Tous les moteurs ont été bloqués et l'origine a renvoyé un défi anti-bot sans contenu de page derrière lui. Réessayez plus tard, ou envoyez allow_degraded: true pour recevoir la page de défi telle quelle. |
Les clés de requête inconnues sont rejetées avec un 422 nommant le champ, sur
/v2/perceive, /v2/perceive/batch, /v2/discover et /v2/lookup sans distinction.
Elles ne sont jamais ignorées silencieusement. Chaque corps 422 contient un tableau
errors de premier niveau avec des messages lisibles par un humain, à côté de la liste
detail brute.
La référence complète des codes de statut se trouve dans le guide des codes d'erreur.
Limites#
| Limite | Valeur |
|---|---|
| Longueur de l'URL | 2,048 caractères |
wait_timeout_ms |
0–60,000 ms |
Longueur de js_code |
20,000 caractères |
| Largeur du viewport | 320–3,840 px |
| Hauteur du 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 lot et par requête | 1,000 (plafond du schéma) |
| Seuil de traitement en ligne | 10 URL (les lots plus importants s'exécutent de manière asynchrone) |
| TTL du cache de résultats | 1 heure |
| Expiration des URL signées | 15 minutes |
| Ops mensuelles (partagées entre tous les endpoints) | 500 / 3 000 / 15 000 / 50 000 selon le palier ; voir la page tarifaire |
Questions fréquentes#
Comment convertir une page web en Markdown avec une API REST ?#
Envoyez POST /v2/perceive avec {"url": "...", "outputs": ["markdown"]}. La page est rendue dans Chrome headless et la réponse contient une URL de téléchargement pré-signée pour le fichier Markdown. Par défaut, only_main_content retire l'habillage du site pour que vous receviez l'article, pas la navigation ; définissez "only_main_content": false pour la page complète, ou ajoutez "direct_download": true pour recevoir les octets du Markdown directement dans le corps de la réponse.
Puis-je obtenir une capture d'écran et du Markdown à partir du même rendu ?#
Oui. outputs accepte n'importe quelle combinaison, donc ["markdown", "screenshot"] (ou screenshot_full_page pour toute la hauteur de défilement) produit les deux à partir d'un seul rendu de navigateur. Vous ne payez jamais deux fois pour la même page en un seul appel.
/v2/perceive effectue-t-il le rendu de pages JavaScript ?#
Oui. Chaque requête effectue un vrai rendu headless-Chrome : les bannières de cookies sont fermées, la page est défilée pour déclencher le contenu en chargement différé, et vous pouvez contrôler la page avant la capture avec wait_for (un sélecteur CSS ou une expression JS), js_code, et block_resources.
Pourquoi mon URL de téléchargement signée a-t-elle cessé de fonctionner ?#
Les URL signées expirent après 15 minutes (expires_in: 900). Récupérez à nouveau l'opération avec GET /v2/perceive/{operation_id} pour obtenir des URL fraîchement signées. Aucun nouveau rendu n'a lieu et aucune op n'est consommée.
Un résultat mis en cache compte-t-il toujours dans mon quota ?#
Oui. Un cache hit facture une op, car le quota mensuel mesure les opérations plutôt que les rendus de navigateur. Définissez cache_mode sur bypass pour ignorer le cache d'1 heure, ou sur refresh pour effectuer un rendu frais et remplacer l'entrée en cache.