Pydantic AI
Build Pydantic AI Twitter API agents for typed tweet search, profiles, followers, and reviewed X actions through TwexAPI MCP.
Build a Pydantic AI Twitter API agent through TwexAPI’s MCP server. Search tweets, inspect profiles, and validate every durable handoff with a Pydantic model. Review every proposed post, reply, like, follow, or direct message.
Why use Pydantic AI with TwexAPI?
Pydantic AI combines model tool calls with typed Python output. TwexAPI supplies Twitter API routes through MCP tools explore and twexapi_request.
| Boundary | Pydantic AI control | Twitter agent benefit |
|---|---|---|
| MCP connection | MCPServerStreamableHTTP |
Keep credentials in your process |
| Final response | Pydantic output_type |
Reject malformed tweet rows and cursors |
| Write actions | Human review step | Pause before posting, replying, or following |
| Connection lifecycle | async with agent |
Reuse one MCP session across related calls |
Use one typed agent for tweet search or profile enrichment. Keep each agent focused.
Prerequisites
- Python 3.10 or later
- A TwexAPI API key
- A Pydantic AI-supported model with tool calling
- A Twitter cookie or
auth_tokenfor write actions
Install
python -m pip install "pydantic-ai[mcp]" python-dotenv
Store secrets outside source control.
export TWEXAPI_API_KEY="YOUR_API_KEY"
export ANTHROPIC_API_KEY="YOUR_ANTHROPIC_KEY"
Build a typed tweet search agent
Define the final handoff before creating the agent. Pydantic AI validates the model output against this schema.
import asyncio
import os
from pathlib import Path
from typing import Literal
from dotenv import load_dotenv
from pydantic import BaseModel
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPServerStreamableHTTP
class TweetRow(BaseModel):
tweet_id: str
text: str
author_username: str | None = None
created_at: str | None = None
url: str | None = None
class TweetSearchHandoff(BaseModel):
query: str
route_used: str
tweets: list[TweetRow]
has_more: bool
next_cursor: str | None = None
stop_reason: Literal["complete", "requested_limit", "cursor_stalled"]
async def main() -> None:
load_dotenv()
server = MCPServerStreamableHTTP(
"https://api.twexapi.io/mcp",
headers={"x-api-key": os.environ["TWEXAPI_API_KEY"]},
)
agent = Agent(
"anthropic:claude-sonnet-4-20250514",
toolsets=[server],
output_type=TweetSearchHandoff,
instructions=(
"Use TwexAPI MCP for Twitter API requests. Call explore before twexapi_request. "
"Preserve exact IDs and cursors. Never invent missing tweet fields. "
"Ask for confirmation before read_only: false actions."
),
)
result = await agent.run(
"Search 25 recent tweets about Pydantic AI MCP. "
"Return the query, route, tweet rows, cursor state, "
"and an explicit stop reason."
)
Path("twexapi-pydantic-ai-handoff.json").write_text(
result.output.model_dump_json(indent=2),
encoding="utf-8",
)
asyncio.run(main())
The agent discovers explore and twexapi_request. Call explore first when the agent does not know a route or parameter shape.
Validate fields before storage
Keep these source fields unchanged in your output model:
- Tweet rows:
tweet_id,text,author_username,created_at,url - Profile rows:
user_id,username,name,description, follower counts - Page state:
has_more,next_cursor, or language-specific cursor names - Write receipts: route name, returned tweet ID, confirmation status
Never cast large IDs to floating-point numbers. Map source IDs to tweet_id or user_id only in your output model.
Continue through an empty page when has_more stays true. Stop when no cursor exists or the server repeats a cursor. Return cursor_stalled with the number of collected rows.
Build a typed handoff
Tweet search
Store query, route, tweet IDs, authors, URLs, has_more, next_cursor, and stop reason.
Profile lookup
Store user_id, username, name, description, and follower counts.
Follower pages
Store source username, follower rows, and cursor checkpoint.
Write actions
Store route, preview text, cookie requirement, and approval record.
Keep API keys outside agent output. See Agent MCP Handoff.
Reuse the MCP connection
Wrap related calls when several runs should share one connection.
async def collect_two_search_pages(agent: Agent) -> None:
async with agent:
first_page = await agent.run(
"Search 25 tweets about Pydantic AI MCP. Preserve the next cursor."
)
cursor = first_page.output.next_cursor
if not first_page.output.has_more or cursor is None:
return
second_page = await agent.run(
f"Continue tweet search for {first_page.output.query!r}. "
f"Use explore, then twexapi_request with cursor {cursor!r}."
)
_ = second_page
Require approval before X actions
Read-only agents can search tweets automatically. Write-capable agents need a human decision before every X action.
class WritePlan(BaseModel):
action: str
endpoint: str
preview_text: str
requires_human_confirmation: bool = True
Stop the agent before calling read_only: false routes. Preview payloads with the CLI --dry-run, then execute through REST or the Python SDK after approval.
Error handling
| Status | Pydantic AI decision |
|---|---|
400 |
Correct the request before retrying |
401 |
Stop and replace the credential |
403 |
Report access or credit issues |
429 |
Back off and preserve the cursor |
5xx |
Retry safe reads with bounded backoff |
Never recreate a pending write after a timeout without checking the returned status.
Choose Pydantic AI MCP or REST
| Requirement | Choose | Reason |
|---|---|---|
| A model selects tweet or profile operations | Pydantic AI MCP | The agent discovers routes with explore |
| Application code calls one known route | Python SDK | The request stays deterministic |
| A human must review an X action | Pydantic AI MCP + manual REST | Pause before execution |
| A scheduled export runs without a model | Prefect or REST | No model decision required |
Package versions
| Package | Supported range |
|---|---|
| Python | >=3.10 |
pydantic-ai |
>=0.8 |
pydantic |
>=2.7 |