How to Ingest Web Pages into OpenAI Assistants

Use EnConvert's Perceive endpoint to fetch live web pages into OpenAI Assistants as clean markdown without manual scraping or site-specific extraction logic

Get API key

The OpenAI Assistants API accepts file uploads and can reason over their contents, but it has no native capability to fetch and read live web pages. Bridging that gap by scraping pages yourself and converting them into uploadable files adds a preprocessing step that breaks differently across page types: JavaScript-rendered pages require a headless browser, content-heavy pages need noise filtering, and parsing logic becomes site-specific across documentation pages, product pages, and news articles. EnConvert's Perceive endpoint handles that step in a single API call, fetching any URL and returning clean markdown with navigation, ads, and boilerplate stripped. The result is a file your pipeline can upload directly to the OpenAI Files API and attach to an Assistant thread, with no intermediate cleaning required.

Example

python
import os

import requests
from openai import OpenAI

ENCONVERT_API_KEY = os.environ["ENCONVERT_API_KEY"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
ASSISTANT_ID = os.environ["ASSISTANT_ID"]  # create one at platform.openai.com/assistants
PAGE_URL = os.environ["PAGE_URL"]

openai_client = OpenAI(api_key=OPENAI_API_KEY)

# Step 1: Fetch clean markdown from any web page via Perceive
response = requests.post(
    "https://api.enconvert.com/v2/perceive",
    headers={
        "X-API-Key": ENCONVERT_API_KEY,
        "Content-Type": "application/json",
    },
    json={
        "url": PAGE_URL,
        "outputs": ["markdown"],
    },
    timeout=120,
)
response.raise_for_status()
perceived = response.json()

if perceived.get("error") or "markdown" not in perceived.get("outputs", {}):
    raise RuntimeError(f"Perceive failed for {PAGE_URL}: {perceived.get('error')}")
if (perceived.get("render_quality") or 0) < 0.5:
    raise RuntimeError(
        f"Low render quality ({perceived.get('render_quality')}): "
        f"{perceived.get('warnings')}"
    )

# The markdown is a signed artifact URL (valid 15 minutes). Plain GET, no API key.
markdown_response = requests.get(perceived["outputs"]["markdown"]["url"], timeout=60)
markdown_response.raise_for_status()
markdown = markdown_response.text

# Optional: preview the extracted markdown before uploading
print(markdown)

# Step 2: Upload the markdown straight from memory to the OpenAI Files API
# (no temp file, so there is nothing to clean up on failure)
uploaded_file = openai_client.files.create(
    file=("page_content.md", markdown.encode("utf-8"), "text/markdown"),
    purpose="assistants",
)

# Step 3: Create a thread and attach the uploaded file to a message
thread = openai_client.beta.threads.create()

openai_client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="Read the attached page and summarise its key points.",
    attachments=[
        {
            "file_id": uploaded_file.id,
            "tools": [{"type": "file_search"}],
        }
    ],
)

# Step 4: Run the Assistant and wait for completion
run = openai_client.beta.threads.runs.create_and_poll(
    thread_id=thread.id,
    assistant_id=ASSISTANT_ID,
)
if run.status != "completed":
    raise RuntimeError(f"Run ended with status {run.status}: {run.last_error}")

# Step 5: Read the response (list() returns newest first)
messages = openai_client.beta.threads.messages.list(thread_id=thread.id)
print(messages.data[0].content[0].text.value)

What you get back

Combining EnConvert with the OpenAI Assistants API gives you any web page as clean, uploadable markdown in one API call, with navigation menus, cookie banners, advertisements, and page boilerplate stripped, and the full content ready for the Assistant to read and reason over without any intermediate handling. Using BeautifulSoup or Playwright to scrape and clean web content before upload requires maintaining selector logic that breaks when a site changes its markup, and a headless browser setup for JavaScript-rendered pages that adds infrastructure overhead and latency to every ingestion call. Playwright also requires manual cleaning of the extracted text before it is usable for LLM consumption, and scraping output varies unpredictably across different page structures, so your cleaning code ends up carrying site-specific exceptions that accumulate over time.

Get free API key — 500 ops per month, no card required