--- title: Tabstack for LangChain (Python) | Tabstack description: Give your LangChain agents reliable web access with langchain-tabstack: schema-enforced extraction, research, generation, and browser automation as native tools. --- [`langchain-tabstack`](https://pypi.org/project/langchain-tabstack/) gives your [LangChain](https://python.langchain.com) agents reliable web access: schema-enforced extraction, multi-source research, AI transformation, and browser automation, all as native LangChain tools backed by the official [`tabstack`](https://pypi.org/project/tabstack/) SDK. LangChain’s built-in loaders (`WebBaseLoader`, `PlaywrightURLLoader`) are fine for prototypes and brittle in production: unpredictable parsing, Playwright binaries to maintain, and behavior that drifts across LangChain releases. This package replaces them with a hosted API. See [Tabstack vs. LangChain browser tools](/comparisons/vs-langchain/index.md) for the full comparison. ## Install - [pip](#tab-panel-239) - [uv](#tab-panel-240) - [poetry](#tab-panel-241) Terminal window ``` pip install langchain-tabstack ``` Terminal window ``` uv add langchain-tabstack ``` Terminal window ``` poetry add langchain-tabstack ``` Requires Python 3.9+. For the agent example below you also need a chat model, for example `pip install langchain langchain-openai`. ## Create and Setup Your API Key Before you can start using Tabstack API, you’ll need to create an API key and set it up in your environment. ### 1. Create Your API Key 1. Visit the [Tabstack Console](https://console.tabstack.ai/) 2. Sign in to your account (or create one if you haven’t already) 3. Navigate to the API Keys section and click the “Manage API Keys” 4. Once you are on the API Keys page, Click “Create New API Key” 5. Give your key a descriptive name (e.g., “Development”, “Production”) and click the “Create API Key” 6. Copy the generated API key and store it securely Your API key will only be shown once. Make sure to copy and store it in a secure location. ### 2. Set Up Environment Variable For security and convenience, we recommend storing your API key as an environment variable rather than hardcoding it in your scripts. macOS/Linux Terminal window ``` # Add to your shell profile (~/.bashrc, ~/.zshrc, or ~/.bash_profile) export TABSTACK_API_KEY="your_api_key_here" # Or set it temporarily for the current session export TABSTACK_API_KEY="your_api_key_here" # Reload your shell or run: source ~/.bashrc # or ~/.zshrc ``` Windows (Command Prompt) ``` # Set temporarily for current session set TABSTACK_API_KEY=your_api_key_here # Set permanently (requires restart) setx TABSTACK_API_KEY "your_api_key_here" ``` Windows (PowerShell) ``` # Set temporarily for current session $env:TABSTACK_API_KEY = "your_api_key_here" # Set permanently for current user [Environment]::SetEnvironmentVariable("TABSTACK_API_KEY", "your_api_key_here", "User") ``` ### 3. Verify Your Setup Test that your environment variable is set correctly: **macOS/Linux/Windows (Git Bash):** Terminal window ``` echo $TABSTACK_API_KEY ``` **Windows (Command Prompt):** Terminal window ``` echo %TABSTACK_API_KEY% ``` **Windows (PowerShell):** Terminal window ``` echo $env:TABSTACK_API_KEY ``` You should see your API key printed in the terminal. ## Quickstart ``` from langchain.agents import create_agent from langchain_tabstack import TABSTACK_TOOLS, TabstackToolError agent = create_agent( "openai:gpt-4o", tools=TABSTACK_TOOLS, system_prompt=( "You are a research assistant with web intelligence tools. " "Use extract_structured_data for specific fields from a URL, " "extract_page_content for full page text, and research_question for multi-source research." ), ) try: result = agent.invoke( {"messages": [{"role": "user", "content": "What are Vercel's pricing plans?"}]} ) print(result["messages"][-1].content) except TabstackToolError as err: print(f"Tabstack failed ({err.status or 'no status'}): {err}") ``` `TABSTACK_TOOLS` reads `TABSTACK_API_KEY` from the environment and is ready to drop into any agent. Using LangChain 0.x? `create_agent` is the LangChain 1.x replacement for `AgentExecutor` + `create_tool_calling_agent`. The tools themselves work with either. ## The tools | Tool | What it does | | -------------------------- | ----------------------------------------------------------------- | | `extract_structured_data` | Pull specific fields from a URL into a JSON shape you define. | | `extract_page_content` | Fetch a page as clean markdown. | | `research_question` | Synthesize a cited answer across multiple pages. | | `generate_structured_data` | Fetch a page, then AI-transform it into derived or reshaped JSON. | | `automate_browser_task` | Run a multi-step, natural-language browser task. | Every tool is exported individually too, so you can use one directly without an agent. Tools return a JSON string (use `json.loads`); `extract_page_content` returns markdown text directly. Each raises `TabstackToolError` on failure; the per-tool examples below omit the `try`/`except` for brevity, see [Error handling](#error-handling). ### extract\_structured\_data ``` import json from langchain_tabstack import extract_structured_data_tool raw = extract_structured_data_tool.invoke( { "url": "https://news.ycombinator.com", # json_schema_json is a JSON-encoded JSON Schema describing the fields you want. "json_schema_json": json.dumps( { "type": "object", "properties": { "stories": { "type": "array", "items": { "type": "object", "properties": { "title": {"type": "string"}, "points": {"type": "number", "description": "Story points"}, }, }, } }, } ), } ) data = json.loads(raw) ``` See [Schema design](/guides/schema-design/index.md) for patterns that produce reliable extraction. ### extract\_page\_content ``` from langchain_tabstack import extract_page_content_tool markdown = extract_page_content_tool.invoke({"url": "https://example.com/blog/article"}) ``` ### research\_question ``` import json from langchain_tabstack import research_question_tool result = json.loads( research_question_tool.invoke( {"query": "What are the latest developments in quantum error correction?"} ) ) print(result["answer"]) for source in result["sources"]: # [{"title": ..., "url": ...}, ...] print(source["url"]) ``` ### generate\_structured\_data ``` import json from langchain_tabstack import generate_structured_data_tool raw = generate_structured_data_tool.invoke( { "url": "https://news.ycombinator.com", "instructions": "For each story, categorize it and write a one-sentence summary.", "json_schema_json": json.dumps( { "type": "object", "properties": { "summaries": { "type": "array", "items": { "type": "object", "properties": { "title": {"type": "string"}, "category": {"type": "string"}, "summary": {"type": "string"}, }, }, } }, } ), } ) ``` ### automate\_browser\_task ``` import json from langchain_tabstack import automate_browser_task_tool result = json.loads( automate_browser_task_tool.invoke( { "task": "Find the top 3 trending repositories and their star counts", "url": "https://github.com/trending", "guardrails": "browse and extract only, do not submit forms", } ) ) # {"answer", "success", "iterations", "actions", "duration_ms", "extracted", "pages_visited"} ``` ## Optional inputs Pass these alongside the required inputs for finer control. They are sent only when provided. - `extract_structured_data`, `extract_page_content`, `generate_structured_data`: - `effort`: `"min"`, `"standard"`, or `"max"`. Use `"max"` for JS-heavy pages (the `PlaywrightURLLoader` replacement). See [Effort levels](/guides/effort-levels/index.md). - `nocache`: `True` to bypass the cache. - `country`: ISO 3166-1 alpha-2 code for [geotargeting](/guides/geotargeting/index.md). - `research_question`: `mode` (`"fast"` or `"balanced"`), `nocache`. - `automate_browser_task`: `guardrails`, `data` (context for form filling), `country`, `max_iterations`, `max_validation_attempts`. ``` # Render a JS-heavy SPA, fresh, from the UK: extract_page_content_tool.invoke( {"url": url, "effort": "max", "nocache": True, "country": "GB"} ) ``` ## Async Every tool supports `ainvoke` natively, backed by `AsyncTabstack`, so it runs in your event loop without a thread-pool bounce: ``` markdown = await extract_page_content_tool.ainvoke({"url": "https://example.com"}) ``` ## Configuration For a custom API key or base URL, or to reuse one client, build the tools explicitly: ``` from langchain_tabstack import create_tabstack_tools tools = create_tabstack_tools(api_key="...") # or pass clients you already have: # create_tabstack_tools(client=..., async_client=...) ``` ## Error handling Tool calls raise `TabstackToolError` with a normalized message and, for API failures, an HTTP `status`: ``` from langchain_tabstack import TabstackToolError, extract_page_content_tool try: extract_page_content_tool.invoke({"url": "https://example.com"}) except TabstackToolError as err: print(f"Tabstack failed ({err.status or 'no status'}): {err}") ``` ## Beyond tools The tools cover the agent use case. For pipelines where Tabstack is a plain step rather than a tool the model chooses, call the [`tabstack` SDK](/sdks/python/quickstart/index.md) directly. ### LCEL chains Wrap a Tabstack call as a `RunnableLambda` and compose it like any other step: ``` import os from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnableLambda from langchain_openai import ChatOpenAI from tabstack import Tabstack client = Tabstack(api_key=os.environ["TABSTACK_API_KEY"]) def fetch_page_content(url: str) -> str: return client.extract.markdown(url=url).content summarize_chain = ( RunnableLambda(fetch_page_content) | ChatPromptTemplate.from_template("Summarize this in 3 bullet points: {text}") | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser() ) print(summarize_chain.invoke("https://example.com/blog/article")) ``` ### RAG document loading Tabstack’s markdown endpoint returns clean content plus optional metadata, ideal for enriching documents before embedding: ``` import os from langchain_core.documents import Document from langchain_community.vectorstores import FAISS from langchain_openai import OpenAIEmbeddings from tabstack import Tabstack client = Tabstack(api_key=os.environ["TABSTACK_API_KEY"]) def tabstack_to_document(url: str) -> Document: result = client.extract.markdown(url=url, metadata=True) meta = result.metadata return Document( page_content=result.content, metadata={ "source": url, "title": meta.title if meta else None, "author": meta.author if meta else None, "published": meta.created_at if meta else None, }, ) urls = ["https://docs.example.com/getting-started", "https://docs.example.com/api-reference"] docs = [tabstack_to_document(u) for u in urls] vectorstore = FAISS.from_documents(docs, OpenAIEmbeddings()) retriever = vectorstore.as_retriever() ``` ## Related - [Integrations overview](/integrations/index.md) - [Tabstack Python SDK](/sdks/python/quickstart/index.md) - [Schema design](/guides/schema-design/index.md) - [Tabstack vs. LangChain browser tools](/comparisons/vs-langchain/index.md) - [LangChain.js integration](/integrations/langchain-js/index.md) and [Vercel AI SDK integration](/integrations/vercel-ai/index.md)