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.

  1. 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.
  2. 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é sous only_main_content. Voir Qualité du Markdown.
  3. 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 un schema et 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.
  4. 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 deductions pour connaître la raison et status_code pour 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 attribut language nu ou un wrapper de coloration syntaxique) puis normalisé, de sorte que ```python arrive 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 YesNo et EvaluationDeploymentProduction ; 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-ignore ou data-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.
Réservé, pas encore actif. 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 warnings plutôt que de dépasser le budget. Sur un plan sans le palier LLM, vous n'obtenez que des données structured heuristiques.


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=markdown diffuse un artefact d'une opération passée. output est 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épond 410.
  • GET /v2/perceive/batch/{job_id}?direct_download=true diffuse le ZIP du lot pour les lots en output_mode: "zip" dont l'archive est prête, et répond 400 sinon.

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.