LangChain vs LangGraph: Which to Use, With the Same X-Monitoring Agent Built in Both

By TwitterAPI.io Team•13 min read
Diagram of the LangChain stack: Deep Agents on top of LangChain create_agent on top of the LangGraph runtime, with LangSmith tracing all three and Langflow shown as a separate visual builder
How the pieces fit: each layer is built on the one below, LangSmith observes all of them, and Langflow sits outside the stack. Package versions from PyPI.

This page compares the frameworks themselves: what LangChain, LangGraph, LangSmith and Langflow each are, and when to reach for which. If you already picked LangChain and only need to load tweets into it, the integration how-to is our LangChain + Twitter (X) API guide (document loader, @tool functions, MCP); this page links to it rather than repeating it.

The short version is that the question changed with LangChain 1.0. Before it, many teams used LangChain chains for simple apps and LangGraph's create_react_agent for agents. Today the LangChain docs describe LangChain as a minimal agent harness built on top of LangGraph, describe LangGraph as a low-level orchestration framework and runtime, and recommend create_agent over create_react_agent. So the real choice is how much of the control flow you want to write yourself.

To make that concrete we build one small agent twice: it watches X (Twitter) for posts about a library and summarizes what developers are asking. The LangChain version is one @tool and one create_agent call. The LangGraph version is a three-node graph that pages through search results in plain code and calls the model only once. Both use the same TwitterAPI.io endpoint, so the data cost is identical and the difference you see is purely the framework.

01 — Section

LangChain vs LangGraph vs LangSmith vs Langflow at a glance

The four names come up together because three of them ship from LangChain Inc and the fourth sounds similar. Here is what each one is according to its own docs and package metadata, read this session.

NameWhat it isInstallCurrent version
LangChainAgent harness: create_agent, tools, middleware, model integrationspip install langchain1.4.7
LangGraphLow-level orchestration runtime: StateGraph, persistence, interruptspip install langgraph1.2.19
LangSmithTracing, evals and deployment (SDK + hosted platform)pip install langsmith0.14.16 (SDK)
Deep AgentsBatteries-included harness built on LangChain agentspip install deepagents0.7.32
LangflowVisual drag-and-drop builder, separate repo langflow-ai/langflowpip install langflow1.13.0

The dependency direction matters. The LangChain overview states that "LangChain's agents are built on top of LangGraph", which is how create_agent gets durable execution, human-in-the-loop support and persistence. The LangGraph overview says LangGraph "can be used without LangChain". So LangChain depends on LangGraph, not the other way round.

LangSmith is a product, not a third framework. Its docs position it to "trace, debug, and evaluate agents built with any of these frameworks". It sits beside your code rather than replacing either library.

Langflow is the odd one out. It is a Python package "with a built-in web application" for assembling flows visually, published from the langflow-ai GitHub organization. If your team wants a canvas rather than code, compare it with Langflow-style tools, not with LangGraph.

02 — Section

What changed in LangChain 1.0: create_agent replaced create_react_agent

Most older comparison posts were written when the advice was "use LangChain for chains, LangGraph for agents". The LangChain v1 migration guide reverses part of that. It says: "Prior to v1.0, we recommended using langgraph.prebuilt.create_react_agent to build agents. Now, we recommend you use langchain.agents.create_agent."

The import path moved. The guide's table summarizes it as "Package moved from langgraph.prebuilt to langchain.agents" and the function was renamed from create_react_agent to create_agent.

The old function now warns. In the LangGraph repository, libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py decorates create_react_agent with @deprecated("create_react_agent has been moved to langchain.agents. Please update your import to from langchain.agents import create_agent.", category=LangGraphDeprecatedSinceV10). The langgraph-prebuilt package (1.1.1 on PyPI) still ships it, so existing code runs, but new code should not start there.

Hooks became middleware. The same guide lists the other renames: prompt became system_prompt, pre- and post-model hooks became middleware with before_model and after_model, tool error handling moved to wrap_tool_call, and the streaming node name changed from "agent" to "model". Legacy code moved to the langchain-classic package.

The practical effect for the comparison: the prebuilt agent you used to import from LangGraph now lives in LangChain, and LangGraph is pitched at the cases the prebuilt agent does not cover.

python
# Before LangChain 1.0 (still runs, emits LangGraphDeprecatedSinceV10)
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(model, tools, prompt="You are helpful.")

# LangChain 1.x
from langchain.agents import create_agent
agent = create_agent(model="anthropic:claude-sonnet-5", tools=tools, system_prompt="You are helpful.")
03 — Section

When to use LangChain vs LangGraph

The LangChain overview itself gives the rule: use LangChain's create_agent "for a highly customizable harness", and use LangGraph, "our low-level orchestration framework, for advanced needs combining deterministic and agentic workflows." Translating that into decisions you actually face:

Your situationStart withWhy
One model, a few tools, loop until doneLangChain create_agentThe loop, tool calling and state are already wired
Need retries, guardrails, dynamic model choiceLangChain + middlewareMiddleware hooks before/after the model and around tool calls
Fixed steps with an LLM in one or two of themLangGraph StateGraphYou write the branches; the model is one node
Pause for human approval, resume days laterLangGraph (or create_agent, which inherits it)Checkpointers and interrupts are LangGraph features
Several agents handing work to each otherLangGraphExplicit nodes and edges keep handoffs auditable
Want a batteries-included harnessDeep AgentsContext compression, virtual filesystem, subagents included

Choose LangChain when the model should drive. If the job is "here are some tools, figure it out", create_agent is less code and you lose nothing: it runs on the LangGraph runtime anyway, so persistence via a checkpointer such as InMemorySaver is still available.

Choose LangGraph when your code should drive. The LangGraph overview lists the reasons: mix deterministic and agentic steps, persistence through failures, human-in-the-loop inspection of state at any point, short- and long-term memory. If you find yourself fighting a prebuilt agent to stop it calling a tool a fifth time, that is the signal to write the graph.

You can move between them. Because create_agent runs on the LangGraph runtime, the two share concepts (state, checkpointers, thread_id), so starting with create_agent and rewriting only the parts that need fixed control flow as a StateGraph later is a modest step, not a rewrite of your tools.

04 — Section

The same X-monitoring agent in LangChain (create_agent)

The task: find recent posts on X about a library and summarize what developers are asking, with links. X data comes from TwitterAPI.io's GET /twitter/tweet/advanced_search, which takes an X search query plus queryType (Latest or Top) and returns a top-level tweets[] array with has_next_page and next_cursor for paging. Reads need only an API key in the X-API-Key header, no X account.

In LangChain the search becomes a @tool and the model decides when to call it and with which query. The docstring is the tool description the model sees, so it carries the query-syntax hint.

What you get. About 50 lines including the tool. The model may rewrite the query, call the tool again, or answer straight away; you steer it with the system prompt and, if needed, middleware. The max_pages argument caps how much it can spend on X data per call.

What you give up. You do not decide the control flow. If the model calls search_x three times with similar queries, you pay for three searches. That is usually fine for an interactive assistant and usually not what you want in a scheduled job.

python
# pip install -U langchain "langchain[anthropic]" requests
import os
import requests
from langchain.agents import create_agent
from langchain.tools import tool

BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}


@tool
def search_x(query: str, max_pages: int = 1) -> list[dict]:
    """Search recent X (Twitter) posts. `query` uses X search syntax,
    for example 'langgraph -filter:retweets lang:en'. Returns compact rows."""
    rows, cursor = [], ""
    for _ in range(max_pages):
        r = requests.get(
            f"{BASE}/twitter/tweet/advanced_search",
            headers=HEADERS,
            params={"query": query, "queryType": "Latest", "cursor": cursor},
            timeout=30,
        )
        r.raise_for_status()
        body = r.json()
        for t in body.get("tweets", []):
            rows.append({
                "url": t["url"],
                "text": t["text"][:280],
                "author": t["author"]["userName"],
                "followers": t["author"]["followers"],
                "likes": t["likeCount"],
                "createdAt": t["createdAt"],
            })
        if not body.get("has_next_page"):
            break
        cursor = body.get("next_cursor", "")
    return rows


agent = create_agent(
    model="anthropic:claude-sonnet-5",
    tools=[search_x],
    system_prompt=(
        "You monitor X for developer questions about a library. "
        "Call search_x once, then summarize the three most useful posts with their URLs."
    ),
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What are developers saying about LangGraph right now?"}]}
)
print(result["messages"][-1].content)
05 — Section

The same agent in LangGraph (StateGraph)

In LangGraph the search is not a tool at all. It is a deterministic fetch node that pages through results in code, a conditional edge decides whether to fetch again, summarize, or stop, and only the summarize node calls the model. The structure follows the LangGraph quickstart: a TypedDict state with Annotated[list[AnyMessage], operator.add] for messages, add_node, add_edge(START, ...), add_conditional_edges, then compile().

What you get. Predictable cost and behavior. The graph makes at most three search requests, stops without calling the model when nothing new was posted, and always sends the model the 15 most-liked posts. For a job that runs every few minutes, that early END is the difference between paying for a model call on every poll and paying only when there is something to read.

What you give up. More code, and you own the edge cases a prebuilt agent handles for you (tool-call parsing, retries, message formatting). For a one-off question, the LangChain version is quicker to write and read.

Persistence is one argument away. Pass a checkpointer to compile() and a thread_id in the config and the graph resumes where it stopped; store cursor in state and the next run continues paging from the last position instead of starting over.

python
# pip install -U langgraph langchain "langchain[anthropic]" requests
import os
import operator
from typing import Literal
from typing_extensions import TypedDict, Annotated

import requests
from langchain.chat_models import init_chat_model
from langchain.messages import AnyMessage, HumanMessage, SystemMessage
from langgraph.graph import StateGraph, START, END

BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
QUERY = "langgraph -filter:retweets lang:en"


class MonitorState(TypedDict):
    messages: Annotated[list[AnyMessage], operator.add]
    posts: list[dict]
    cursor: str
    pages: int


def fetch(state: MonitorState) -> dict:
    """Deterministic step: no LLM, just one page of advanced_search."""
    r = requests.get(
        f"{BASE}/twitter/tweet/advanced_search",
        headers=HEADERS,
        params={"query": QUERY, "queryType": "Latest", "cursor": state.get("cursor", "")},
        timeout=30,
    )
    r.raise_for_status()
    body = r.json()
    new = [
        {"url": t["url"], "text": t["text"][:280], "likes": t["likeCount"],
         "followers": t["author"]["followers"]}
        for t in body.get("tweets", [])
    ]
    return {
        "posts": state.get("posts", []) + new,
        "cursor": body.get("next_cursor", "") if body.get("has_next_page") else "",
        "pages": state.get("pages", 0) + 1,
    }


def enough(state: MonitorState) -> Literal["fetch", "summarize", END]:
    """Branch in code: stop early when nothing came back, page at most 3 times."""
    if not state["posts"]:
        return END
    if state["cursor"] and len(state["posts"]) < 40 and state["pages"] < 3:
        return "fetch"
    return "summarize"


model = init_chat_model("anthropic:claude-sonnet-5", temperature=0)


def summarize(state: MonitorState) -> dict:
    """Agentic step: the only node that calls the model."""
    top = sorted(state["posts"], key=lambda p: p["likes"], reverse=True)[:15]
    prompt = SystemMessage(content="Summarize what developers are asking about. Cite post URLs.")
    reply = model.invoke([prompt, HumanMessage(content=str(top))])
    return {"messages": [reply]}


builder = StateGraph(MonitorState)
builder.add_node("fetch", fetch)
builder.add_node("summarize", summarize)
builder.add_edge(START, "fetch")
builder.add_conditional_edges("fetch", enough, ["fetch", "summarize", END])
builder.add_edge("summarize", END)
graph = builder.compile()

out = graph.invoke({"messages": [], "posts": [], "cursor": "", "pages": 0})
print(f"{len(out['posts'])} posts fetched in {out['pages']} request(s)")
if out["messages"]:
    print(out["messages"][-1].content)
06 — Section

What the X data costs in either version

The framework does not change the data bill; the number of search calls does. TwitterAPI.io bills tweet endpoints at 15 credits per tweet returned, which is $0.15 per 1,000 tweets ($1 = 100,000 credits), with a minimum of 15 credits per request. A full 20-tweet page of advanced_search costs $0.003, and a search that finds nothing costs the $0.00015 minimum. You pay only for the data returned.

ScenarioTwitterAPI.ioOfficial X API (pay-per-usage)
1 search page, 20 tweets$0.003$0.10 (20 x $0.005 per post read)
1,000 tweets$0.15$5.00
Empty poll every 5 minutes, 30 days$1.30depends on posts returned
Empty poll every 60 seconds, 30 days$6.48 (43,200 checks)depends on posts returned

The official X API figures are from the pay-per-usage rate card on docs.x.com/x-api/getting-started/pricing: $0.005 per post read, $0.010 per user read, with pay-per-usage plans capped at 3 million post reads per monthly billing cycle (Enterprise above that). Per 1,000 tweets that is $5 against $0.15, about 33 times more.

Where the frameworks differ. The LangGraph version above never makes more than three search requests and skips the model when the first page is empty. The LangChain version can make as many as the model chooses, each up to max_pages. If you poll on a schedule, put the cap in code: a graph edge, or a max_pages limit plus a middleware that stops repeated tool calls.

Rate limits. TwitterAPI.io allows 0.2 requests per second before the first payment, 20 once paid, 50 on Scale, 500 on Business and up to 1,000 on request. Subscriptions run from Starter at $29 to Business at $1,499 a month; see the pricing page for what each includes. New accounts that pass the automatic bot check get up to $1 of free credits (at most 100,000 credits, no card, one per person or network, expiring after 31 days). Purchased credits never expire.

07 — Section

What LangChain's own X accounts talk about (real-data study)

If LangGraph is the runtime and LangChain the harness, which one does the company actually promote? We pulled the recent timelines of the company account @LangChain (renamed from @LangChainAI, which now returns "user not found"), the open-source account @LangChain_OSS, and CEO Harrison Chase (@hwchase17): 1,400 posts, of which 649 are original posts or quotes and 751 are reposts. Each original post was tagged by keyword for the products it names.

Mentioned in original postsShareMedian likesMedian views
LangGraph9.9%447,862
LangSmith23.3%338,968
Deep Agents24.3%40.59,136
"LangChain" (framework or company)43.3%348,768
None of these34.2%35.59,294

LangGraph is the quiet layer. It appears in 9.9% of original posts, behind LangSmith (23.3%) and Deep Agents (24.3%). On the company account it is 4.5%; on @LangChain_OSS it is 29.1%, and 60.9% of all LangGraph mentions come from that open-source account. That matches the docs' framing: LangGraph is infrastructure, while the company's marketing sits on the harness (Deep Agents, create_agent) and the paid platform (LangSmith).

LangGraph is rarely mentioned alone. 71.9% of posts that name LangGraph also name LangChain, which fits the stack picture: the two ship together.

But LangGraph posts get engagement. They have the highest median likes of any topic (44) despite a lower median view count, and 43.8% of them link to docs, GitHub or the blog, the highest share of any topic. That suggests the runtime still draws interest from the developers who need it.

Account profiles differ. @LangChain posted about 6.84 times a day over the sample window and reposts 32.6% of the time; @hwchase17 reposts 70.3% of the time and quotes in 77.5% of his original posts, so his own text is a small slice of his timeline.

Grouped bar chart: share of original posts mentioning LangGraph, LangChain, LangSmith and Deep Agents for @LangChain, @LangChain_OSS and @hwchase17. LangGraph is lowest at 9.9% pooled.
Share of original posts naming each product, by account (649 original posts, collected in 2026). A post can name several products.
08 — Section

Engagement by topic and how we collected this

Question. Which layer of the Lang* stack do LangChain's own accounts talk about, and how do those posts perform?

Accounts and endpoint. @LangChain (company), @LangChain_OSS (open-source projects) and @hwchase17 (CEO, a public figure), via TwitterAPI.io /twitter/user/last_tweets, newest first, without replies: 25, 15 and 30 pages respectively, 1,400 posts in total. Collected in 2026; each account's window runs from its oldest to newest sampled post (73 days for @LangChain, 274 for @LangChain_OSS, 102 for @hwchase17).

Method. Reposts were excluded. For each original post we removed @handles and URLs from the text, added the expanded link URLs, and matched langgraph, langsmith, deep agent(s)/deepagents, langflow and the word langchain case-insensitively. A post can carry several labels. The word "LangChain" counts both the framework and the company, so that row is an upper bound for framework mentions.

Cost. 72 requests returning 1,400 tweets, about $0.21 of credits at 15 credits per tweet.

What the sample cannot show. Keyword tags are not intent: a post naming LangGraph may be a release note or a passing mention. Three accounts over a few months are not the developer community at large, and Langflow appears in zero posts; it is published from a different GitHub organization. Engagement depends on media, timing and who reposts, not only on topic.

Privacy. Aggregates only, from an organization account, an open-source project account and a public figure. Reposted and quoted authors were not analyzed; raw timelines stay outside the repository and the committed sample has no IDs, URLs, text or handles.

Horizontal bar chart of median likes per original post by topic: LangGraph 44, Deep Agents 40.5, LangChain 34, LangSmith 33, none 35.5
Median likes and views per original post by topic, pooled across three accounts (collected in 2026).
09 — Section

Shipping it: tracing with LangSmith and posting results back to X

Tracing. Whichever version you pick, LangSmith can trace it; the LangChain overview links a tracing quickstart under docs.langchain.com/langsmith for the setup. The LangSmith pricing page lists the Developer plan at $0 per seat (one seat, up to 5k base traces a month, then pay-as-you-go) and Plus at $39 per seat a month (up to 10k base traces, unlimited seats); Enterprise is custom. A monitoring graph that runs every 5 minutes produces 8,640 runs a month, so decide early whether every run needs a trace or only the ones that reach summarize.

Posting a digest back to X (optional). TwitterAPI.io also has write endpoints, which run through an authenticated session: one /twitter/user_login_v2 login ($0.10, with a proxy you supply) returns login_cookies, and each post via /twitter/create_tweet_v2 then costs 60 credits ($0.0006). The call acts as that account, so X can rate-limit, lock or suspend it; keep a human pace (a daily digest, not a post per run), expect that the session can need a re-login, and note that writes are billed per HTTP 200 call. In LangGraph this is one more node after summarize, gated by a human-in-the-loop interrupt if you want approval before anything is published.

Getting a key. Create an API key on the dashboard, put it in TWITTERAPI_IO_KEY, and run either script above. The endpoint reference and response fields are on docs.twitterapi.io; reads need no X account.

python
# pip install -U langgraph langchain "langchain[anthropic]" requests
import os
import operator
from typing import Literal
from typing_extensions import TypedDict, Annotated

import requests
from langchain.chat_models import init_chat_model
from langchain.messages import AnyMessage, HumanMessage, SystemMessage
from langgraph.graph import StateGraph, START, END

BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
QUERY = "langgraph -filter:retweets lang:en"


class MonitorState(TypedDict):
    messages: Annotated[list[AnyMessage], operator.add]
    posts: list[dict]
    cursor: str
    pages: int


def fetch(state: MonitorState) -> dict:
    """Deterministic step: no LLM, just one page of advanced_search."""
    r = requests.get(
        f"{BASE}/twitter/tweet/advanced_search",
        headers=HEADERS,
        params={"query": QUERY, "queryType": "Latest", "cursor": state.get("cursor", "")},
        timeout=30,
    )
    r.raise_for_status()
    body = r.json()
    new = [
        {"url": t["url"], "text": t["text"][:280], "likes": t["likeCount"],
         "followers": t["author"]["followers"]}
        for t in body.get("tweets", [])
    ]
    return {
        "posts": state.get("posts", []) + new,
        "cursor": body.get("next_cursor", "") if body.get("has_next_page") else "",
        "pages": state.get("pages", 0) + 1,
    }


def enough(state: MonitorState) -> Literal["fetch", "summarize", END]:
    """Branch in code: stop early when nothing came back, page at most 3 times."""
    if not state["posts"]:
        return END
    if state["cursor"] and len(state["posts"]) < 40 and state["pages"] < 3:
        return "fetch"
    return "summarize"


model = init_chat_model("anthropic:claude-sonnet-5", temperature=0)


def summarize(state: MonitorState) -> dict:
    """Agentic step: the only node that calls the model."""
    top = sorted(state["posts"], key=lambda p: p["likes"], reverse=True)[:15]
    prompt = SystemMessage(content="Summarize what developers are asking about. Cite post URLs.")
    reply = model.invoke([prompt, HumanMessage(content=str(top))])
    return {"messages": [reply]}


builder = StateGraph(MonitorState)
builder.add_node("fetch", fetch)
builder.add_node("summarize", summarize)
builder.add_edge(START, "fetch")
builder.add_conditional_edges("fetch", enough, ["fetch", "summarize", END])
builder.add_edge("summarize", END)
graph = builder.compile()

out = graph.invoke({"messages": [], "posts": [], "cursor": "", "pages": 0})
print(f"{len(out['posts'])} posts fetched in {out['pages']} request(s)")
if out["messages"]:
    print(out["messages"][-1].content)
10 — Questions

Questions readers ask

Is LangGraph replacing LangChain?

No. The LangChain docs say LangChain's agents are built on top of LangGraph, and LangGraph's docs call it a low-level orchestration framework that can also be used without LangChain. They are layers of one stack: LangChain's create_agent is the higher-level harness, LangGraph the runtime underneath.

Should a beginner learn LangChain or LangGraph first?

The LangGraph overview itself recommends getting familiar with models and tools before using LangGraph, and the LangChain overview points new agents at create_agent. Start with LangChain, then drop to LangGraph when you need to control the steps yourself.

Is create_react_agent deprecated?

Yes. The LangChain v1 migration guide replaces langgraph.prebuilt.create_react_agent with langchain.agents.create_agent, and the LangGraph source marks create_react_agent with a LangGraphDeprecatedSinceV10 deprecation warning. It still runs from langgraph-prebuilt, but new code should use create_agent.

What is the difference between LangChain, LangGraph and LangSmith?

LangChain is the agent harness (model, tools, prompt, middleware). LangGraph is the orchestration runtime (graphs, state, persistence, interrupts). LangSmith is the platform for tracing, evaluating and deploying agents built with either; it has a free Developer plan and a Plus plan at $39 per seat a month.

How does Langflow compare with LangGraph?

Langflow is a visual builder, a Python package with a built-in web application published from the separate langflow-ai/langflow repository. LangGraph is a code-first runtime. Choose Langflow if you want to assemble flows on a canvas, LangGraph if you want typed state and control flow in Python.

Can I use LangGraph without LangChain?

Yes. The LangGraph overview states it is built by LangChain Inc but can be used without LangChain. In practice most examples import message types and init_chat_model from langchain, as the quickstart does, but the graph itself only needs langgraph.

How do I give a LangChain or LangGraph agent live X (Twitter) data?

Wrap a read endpoint such as /twitter/tweet/advanced_search in a @tool for create_agent, or call it from a node in a StateGraph. On TwitterAPI.io that costs $0.15 per 1,000 tweets returned, a 20-tweet page $0.003; the official X API's pay-per-usage rate card is $0.005 per post read.

Is OpenAI Agents SDK an alternative to LangGraph?

It is OpenAI's own agent framework, so it is closer in level to LangChain's create_agent (an agent loop with tools) than to LangGraph's graph runtime. Our OpenAI Agents SDK guide shows the same kind of X-data tool built with that SDK, so you can compare the code directly.

11 — Further reading

Continue

Sources & further reading
More from this series
Build it

Stop reading. Start building.

Starter credits cover real testing on real data. Google sign-in, no card, no application queue.

Get an API key

Already building with TwitterAPI.io? Publish an honest tutorial or build log about it and earn up to $200 a month in API credits.