{"id":114,"date":"2026-01-26T07:38:13","date_gmt":"2026-01-25T23:38:13","guid":{"rendered":"https:\/\/firooz.us\/blog\/?p=114"},"modified":"2026-01-26T08:00:38","modified_gmt":"2026-01-26T00:00:38","slug":"a-minimal-agentic-workflow-find-an-api-write-code-validate","status":"publish","type":"post","link":"https:\/\/firooz.us\/blog\/index.php\/2026\/01\/26\/a-minimal-agentic-workflow-find-an-api-write-code-validate\/","title":{"rendered":"A Minimal Agentic Workflow: Find an API, Write Code, Validate"},"content":{"rendered":"\n<p>If you\u2019ve ever tried to go from <em>\u201cI need an API for X\u201d<\/em> to <em>\u201chere\u2019s working code that calls it\u201d<\/em>, you know the painful parts: searching, skimming docs, figuring out auth, and then discovering your code doesn\u2019t actually run.<\/p>\n\n\n\n<p>I took the challenge to automate this loop using a small and practical agentic workflow based on <a href=\"https:\/\/www.langchain.com\/langgraph\">langGraph<\/a>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>generate search keywords from a user task<\/li>\n\n\n\n<li>search the web for candidate docs\/pages<\/li>\n\n\n\n<li>fetch and filter pages that likely contain a usable API<\/li>\n\n\n\n<li>generate runnable code using the selected pages<\/li>\n\n\n\n<li>execute the code as a validation step<\/li>\n\n\n\n<li>retry search if nothing works<\/li>\n<\/ol>\n\n\n\n<p>The goal isn\u2019t \u201ca fully autonomous agent.\u201d It\u2019s a <strong>debuggable, extendable baseline<\/strong> you can build on.<\/p>\n\n\n\n<p><strong>Code:<\/strong> GitHub repo (link) \u2192 <a href=\"https:\/\/github.com\/mamhamed\/Agentic\/blob\/main\/Agentic_learn_API.ipynb\">https:\/\/github.com\/mamhamed\/Agentic\/blob\/main\/Agentic_learn_API.ipynb<\/a><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Task Template<\/h2>\n\n\n\n<p>The workflow is designed for a generic task template:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Task Template:<\/strong> Find an API that can solve <code>{task}<\/code>, then write code using that API.<\/p>\n<\/blockquote>\n\n\n\n<p>Example tasks:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u201cI want to book flights. What APIs have a free tier, and how do I call them in Python?\u201d<\/li>\n\n\n\n<li>\u201cFind a free-tier API for hotel prices and write sample code.\u201d<\/li>\n\n\n\n<li>\u201cFind an API for housing sales data and show how to query it.\u201d<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The High-Level Flow<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"326\" src=\"https:\/\/firooz.us\/blog\/wp-content\/uploads\/2026\/01\/api_finder_agent-1024x326.png\" alt=\"\" class=\"wp-image-117\" srcset=\"https:\/\/firooz.us\/blog\/wp-content\/uploads\/2026\/01\/api_finder_agent-1024x326.png 1024w, https:\/\/firooz.us\/blog\/wp-content\/uploads\/2026\/01\/api_finder_agent-300x96.png 300w, https:\/\/firooz.us\/blog\/wp-content\/uploads\/2026\/01\/api_finder_agent-768x245.png 768w, https:\/\/firooz.us\/blog\/wp-content\/uploads\/2026\/01\/api_finder_agent-1536x489.png 1536w, https:\/\/firooz.us\/blog\/wp-content\/uploads\/2026\/01\/api_finder_agent-2048x652.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>Here\u2019s the flow I implemented using <strong>LangGraph<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Task \u2192 Keywords<\/strong>: An LLM turns the user\u2019s request into search-friendly keywords.<\/li>\n\n\n\n<li><strong>Web Search<\/strong>: A search tool (Brave Search in my prototype) returns a batch of URLs.<\/li>\n\n\n\n<li><strong>Fetch &amp; Filter<\/strong>: The workflow fetches each URL, extracts the readable text, and asks an LLM: <em>\u201cDoes this page contain an API I can use to solve the task?\u201d<\/em><\/li>\n\n\n\n<li><strong>Orchestration<\/strong>: A central router decides whether to keep searching or move forward.<\/li>\n\n\n\n<li><strong>Write Code<\/strong>: If we have a promising page, an LLM generates Python code to call the API.<\/li>\n\n\n\n<li><strong>Code Exec<\/strong>: The workflow executes the generated code. If it fails, we retry with the next search batch.<\/li>\n<\/ul>\n\n\n\n<p>Conceptually, this turns \u201cAPI discovery\u201d into an iterative pipeline with an explicit success signal: <strong>the code actually runs<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why a Graph (Not a Monolithic Agent)<\/h2>\n\n\n\n<p>This design intentionally uses a <strong>hard-coded DAG<\/strong> rather than a free-form planner. In other words:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The \u201cplan\u201d is encoded in the graph structure (keywords \u2192 search \u2192 fetch\/filter \u2192 code \u2192 exec).<\/li>\n\n\n\n<li>The \u201cintelligence\u201d is concentrated in a few places (keyword generation, relevance filtering, code generation).<\/li>\n\n\n\n<li>The \u201ccontrol\u201d lives in an orchestrator node that decides what to do next based on state.<\/li>\n<\/ul>\n\n\n\n<p>This makes the system:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>easier to debug<\/strong> (every node has a single responsibility)<\/li>\n\n\n\n<li><strong>easier to extend<\/strong> (swap search provider, add better extraction, add scoring, etc.)<\/li>\n\n\n\n<li><strong>less brittle<\/strong> than a single prompt trying to do everything end-to-end<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Nodes (What Each Agent Does)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1) Keywords Node<\/h3>\n\n\n\n<p>Takes the user task and produces search keywords. This helps keep search queries short and high-signal.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2) Search Node<\/h3>\n\n\n\n<p>Uses a web search API to retrieve a set of URLs (with pagination support). If the first batch doesn\u2019t yield anything good, the system can request the next batch.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3) Fetch &amp; Filter Node<\/h3>\n\n\n\n<p>For each URL:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>fetch HTML<\/li>\n\n\n\n<li>strip scripts\/styles<\/li>\n\n\n\n<li>extract text from the main content region<\/li>\n\n\n\n<li>run a lightweight LLM filter: \u201cYES\/NO \u2014 is this page likely to contain a usable API for the task?\u201d<\/li>\n<\/ul>\n\n\n\n<p>The output is a list of pages annotated with <code>good_page<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4) Orchestrator Node<\/h3>\n\n\n\n<p>Implements the routing policy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If any page looks good \u2192 go to code generation<\/li>\n\n\n\n<li>If none look good \u2192 go back to search and try another batch<\/li>\n\n\n\n<li>If there\u2019s nothing to process \u2192 end<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5) Code Node<\/h3>\n\n\n\n<p>Reads the relevant page content and generates Python code. I constrain the model to return JSON:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{ \"language\": \"python\", \"code\": \"...\", \"note\": \"...\" }\n<\/code><\/pre>\n\n\n\n<p>This makes parsing and evaluation much more reliable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6) Code Exec Node<\/h3>\n\n\n\n<p>Executes the model-generated code. If it succeeds, we\u2019re done. If it fails, we treat that as a strong negative signal and retry search.<\/p>\n\n\n\n<p>This \u201cexecute-to-validate\u201d step is the key difference between \u201cLLM demo code\u201d and \u201ccode you can actually run.\u201d<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What Works Well<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Simple and effective retry loop:<\/strong> If the web results are bad (no API info, not free, etc), the workflow keeps searching.<\/li>\n\n\n\n<li><strong>Grounded code generation:<\/strong> Code is generated from specific page content (docs\/pricing\/examples).<\/li>\n\n\n\n<li><strong>Concrete success metric:<\/strong> Code execution provides a real validation step.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>This workflow is a compact baseline for an agent that can:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>discover APIs on the web,<\/li>\n\n\n\n<li>write code to call them,<\/li>\n\n\n\n<li>and validate that code via execution,<\/li>\n\n\n\n<li>with a clean loop and clear decision points.<\/li>\n<\/ul>\n\n\n\n<p>If you want a minimal starting point for agentic systems that are <em>debuggable and practical<\/em>, this is a good place to begin.<\/p>\n\n\n\n<p><strong>Code:<\/strong> GitHub repo (link) \u2192 <a href=\"https:\/\/github.com\/mamhamed\/Agentic\/blob\/main\/Agentic_learn_API.ipynb\">https:\/\/github.com\/mamhamed\/Agentic\/blob\/main\/Agentic_learn_API.ipynb<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you\u2019ve ever tried to go from \u201cI need an API for X\u201d to \u201chere\u2019s working code that calls it\u201d, you know the painful parts: searching, skimming docs, figuring out auth, and then discovering your code doesn\u2019t actually run. I took the challenge to automate this loop using a small and practical agentic workflow based &#8230; <a title=\"A Minimal Agentic Workflow: Find an API, Write Code, Validate\" class=\"read-more\" href=\"https:\/\/firooz.us\/blog\/index.php\/2026\/01\/26\/a-minimal-agentic-workflow-find-an-api-write-code-validate\/\" aria-label=\"More on A Minimal Agentic Workflow: Find an API, Write Code, Validate\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[21,7,16],"class_list":["post-114","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-agentic","tag-ai-2","tag-technical-notes"],"_links":{"self":[{"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/posts\/114","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=114"}],"version-history":[{"count":3,"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/posts\/114\/revisions"}],"predecessor-version":[{"id":119,"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/posts\/114\/revisions\/119"}],"wp:attachment":[{"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=114"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=114"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/firooz.us\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=114"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}