<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: mv7</title>
    <description>The latest articles on Forem by mv7 (@mv7).</description>
    <link>https://forem.com/mv7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3929492%2Fe07d8d7c-79e0-4156-8678-9dab10e41921.png</url>
      <title>Forem: mv7</title>
      <link>https://forem.com/mv7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mv7"/>
    <language>en</language>
    <item>
      <title>I gave the OpenAI SDK live web search by changing one line</title>
      <dc:creator>mv7</dc:creator>
      <pubDate>Fri, 15 May 2026 11:12:33 +0000</pubDate>
      <link>https://forem.com/mv7/i-gave-the-openai-sdk-live-web-search-by-changing-one-line-1pd0</link>
      <guid>https://forem.com/mv7/i-gave-the-openai-sdk-live-web-search-by-changing-one-line-1pd0</guid>
      <description>&lt;p&gt;Last week I was stitching together Serper for web + Tavily for synthesis + a YouTube transcript API just to answer one chat question. Every category was a new API contract, a new auth flow, a new JSON shape my agent had to know about.&lt;/p&gt;

&lt;p&gt;I almost wrote a wrapper SDK. Then I realized I didn't need to — the &lt;code&gt;openai&lt;/code&gt; SDK can already do all of this. You just point it at a different &lt;code&gt;base_url&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here's the trick
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pxs_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                        &lt;span class="c1"&gt;# pixserp key, not OpenAI
&lt;/span&gt;    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://pixserp.com/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# one URL change
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pixserp-standard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EU AI Act enforcement timeline 2026&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;citations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The &lt;code&gt;openai&lt;/code&gt; SDK doesn't know it's talking to a search backend. It thinks it's calling &lt;code&gt;chat.completions.create&lt;/code&gt;. The server does the routing — runs the searches, fetches pages, synthesizes a cited answer — and returns a standard OpenAI assistant message.&lt;/p&gt;

&lt;p&gt;Inline &lt;code&gt;[1]&lt;/code&gt;, &lt;code&gt;[2]&lt;/code&gt; markers in &lt;code&gt;content&lt;/code&gt;. Structured &lt;code&gt;message.citations&lt;/code&gt; array with one entry per source. Drop into your existing typed code, render in your existing UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works at all
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;openai&lt;/code&gt; SDK doesn't validate response shape strictly — it expects the OpenAI wire format and serializes whatever comes back into a &lt;code&gt;ChatCompletion&lt;/code&gt; object. As long as the server returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;choices[*].message.role = "assistant"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;choices[*].message.content&lt;/code&gt; (the cited text)&lt;/li&gt;
&lt;li&gt;Standard &lt;code&gt;usage&lt;/code&gt; block&lt;/li&gt;
&lt;li&gt;Optional extra fields (citations, cost) accessible via &lt;code&gt;model_extra&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…it just works. Same SSE streaming for &lt;code&gt;stream=True&lt;/code&gt;. Same &lt;code&gt;response_format: { type: "json_schema" }&lt;/code&gt; for structured output. Same &lt;code&gt;tool_calls&lt;/code&gt; shape on the response if the backend wants to surface internal steps.&lt;/p&gt;

&lt;p&gt;A search backend that &lt;em&gt;speaks chat-completions&lt;/em&gt; is a drop-in for any code that already speaks OpenAI. No wrapper, no new SDK, no second auth flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than just "web"
&lt;/h2&gt;

&lt;p&gt;The thing that pushed me over the edge to drop the wrapper plan: pixserp routes to ten different answer shapes through the same endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pixserp-standard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;citations&lt;/span&gt;

&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cheapest direct MXP→JFK on July 18 in economy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hotels in Barcelona Jul 15–20, 4 stars or more&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize https://www.youtube.com/watch?v=jNQXAC9IVRw in 5 bullets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iPhone 15 Pro under $900 with free shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Best ramen near Porta Garibaldi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flights, hotels, places, shopping, transcripts, news, images — all the same call. The &lt;code&gt;citations&lt;/code&gt; array carries per-shape structured fields: &lt;code&gt;rating&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;gps&lt;/code&gt;, &lt;code&gt;hours&lt;/code&gt;, &lt;code&gt;airline&lt;/code&gt;, &lt;code&gt;check_in&lt;/code&gt;. Your renderer can show real cards instead of bare links.&lt;/p&gt;

&lt;p&gt;I'm not using all ten verticals in my app yet. But I'm not paying a "stitching tax" anymore either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming works the way you expect
&lt;/h2&gt;

&lt;p&gt;The bit that mattered most for the chat UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pixserp-fast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What did the Fed announce today?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard OpenAI SSE. The &lt;code&gt;delta&lt;/code&gt; shape is the one your code already handles. Citations land on the final chunk in &lt;code&gt;delta.citations&lt;/code&gt; (or via &lt;code&gt;model_extra&lt;/code&gt; depending on your SDK version) so the references panel can render once the answer settles.&lt;/p&gt;

&lt;p&gt;I dropped a custom EventSource handler I'd written for a different vendor — back to the stock openai stream parser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-call cost on a header
&lt;/h2&gt;

&lt;p&gt;The bit dev.to readers ask about most after "does it work": cost visibility. Pixserp puts the per-call cost on the response header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;x-cost-usd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;decimal value per request&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stick that into your structured logs and you've got per-call cost-per-customer, per-feature, per-route — without paying for a third-party billing analytics tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas (the honest part)
&lt;/h2&gt;

&lt;p&gt;Five things I tripped over while migrating:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't bring your own retries.&lt;/strong&gt; The server handles upstream-vendor retries (Google Flights timing out, etc.) and surfaces a definitive answer or a definitive error. Wrapping a retry loop around &lt;code&gt;chat.completions.create&lt;/code&gt; in your app double-counts and burns credit. Trust the server, fail fast on 4xx.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;tool_calls&lt;/code&gt; are &lt;em&gt;not&lt;/em&gt; OpenAI function calls.&lt;/strong&gt; If the response includes a &lt;code&gt;tool_calls&lt;/code&gt; array, those are the internal retrieval steps surfaced for transparency (which page fetches, which searches). They are NOT a request for &lt;em&gt;you&lt;/em&gt; to execute a function — there's no tool-loop to close. Don't &lt;code&gt;submit_tool_outputs&lt;/code&gt;. Render them or ignore them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context windows are smaller than chat models.&lt;/strong&gt; Search synthesis is fast and short by design; if you throw a 30k-token system prompt at it, you're paying for tokens you won't use. Keep the user message focused on the question.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;response_format: json_schema&lt;/code&gt; is web-grounded, not free-form.&lt;/strong&gt; If you ask for &lt;code&gt;{"answer": str, "year": int}&lt;/code&gt; and the live web doesn't know the year, the field will be &lt;code&gt;null&lt;/code&gt; — not hallucinated. That's the feature, but worth knowing if your downstream code expects a non-null value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streaming &lt;code&gt;delta.citations&lt;/code&gt; shape depends on SDK version.&lt;/strong&gt; &lt;code&gt;pip show openai&lt;/code&gt; ≥ 1.55 exposes them in the typed delta. Older versions: read citations from &lt;code&gt;r.choices[0].message.model_extra&lt;/code&gt; after the stream completes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;If you're already calling &lt;code&gt;openai.chat.completions.create&lt;/code&gt;, adding live web search to your agent is a &lt;code&gt;base_url&lt;/code&gt; swap and a model-name change. No new SDK. No new auth pattern. No "we'll do it next sprint" because the integration shape changed.&lt;/p&gt;

&lt;p&gt;For the full reference see &lt;a href="https://pixserp.com/docs" rel="noopener noreferrer"&gt;pixserp's docs&lt;/a&gt;. Free credit on signup if you want to throw a real workload at it — no card.&lt;/p&gt;

&lt;p&gt;I'm shipping the rest of my agent's verticals on top of this now. If you've been stitching three or four search APIs together for one chat reply, take fifteen minutes and try the swap. It's the smallest amount of code I've ever deleted to add a feature.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;pixserp is an OpenAI-compatible AI search API — &lt;a href="https://pixserp.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; · &lt;a href="https://pixserp.com/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;. Built at &lt;a href="https://teti.ai" rel="noopener noreferrer"&gt;Teti AI&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>openai</category>
      <category>ai</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Meet pixserp — One Drop-in API for Web, News, Places, Flights, Hotels, YouTube and Anything Else on the Live Web</title>
      <dc:creator>mv7</dc:creator>
      <pubDate>Wed, 13 May 2026 14:48:26 +0000</pubDate>
      <link>https://forem.com/mv7/meet-pixserp-one-drop-in-api-for-web-news-places-flights-hotels-youtube-and-anything-else-on-4l0d</link>
      <guid>https://forem.com/mv7/meet-pixserp-one-drop-in-api-for-web-news-places-flights-hotels-youtube-and-anything-else-on-4l0d</guid>
      <description>&lt;p&gt;If you've built an AI agent in 2026, you've probably integrated more "search" APIs than you'd like to admit. One for web pages. One for news. One for product prices because the SERP one doesn't return shopping cards. One for flights, because &lt;em&gt;of course&lt;/em&gt; none of the above know about flights. One for YouTube transcripts. Each with its own SDK, its own JSON shape, its own pricing model, its own bill at month-end.&lt;/p&gt;

&lt;p&gt;We were there too. We built &lt;a href="https://teti.ai" rel="noopener noreferrer"&gt;Teti AI&lt;/a&gt; — an AI assistant used daily by hundreds of thousands of people — and behind every conversation there's a live web lookup. Millions a day. Sometimes it's a news headline. Sometimes a flight, a hotel, a product, a YouTube summary. &lt;strong&gt;All of those are "search" from the user's point of view, even if no single search API covers them.&lt;/strong&gt; That's where pixserp was born.&lt;/p&gt;

&lt;p&gt;This post is the short version of what pixserp is and why we think you should care if you're shipping anything LLM-shaped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup: one endpoint, ten shapes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pixserp.com" rel="noopener noreferrer"&gt;pixserp&lt;/a&gt; is an AI search API with a single twist: &lt;strong&gt;one endpoint covers ten different shapes of answer&lt;/strong&gt;. You write a natural-language question; the agent figures out which vertical the answer lives in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pxs_…&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://pixserp.com/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pixserp-fast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;

&lt;span class="c1"&gt;# Same call. Different shapes of answer. One bill.
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Best practices for Postgres index maintenance in 2026&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# web
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest news on AI startup funding this week&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="c1"&gt;# news
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top-rated ramen near Porta Garibaldi, Milan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="c1"&gt;# places
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iPhone 15 Pro under $900 with free shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="c1"&gt;# shopping
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cheapest direct MXP→JFK on July 18, economy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="c1"&gt;# flights
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotels in Barcelona Jul 15-20, 4★+, under $250/night&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# hotels
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summarize https://youtu.be/dQw4w9WgXcQ in 5 bullets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# YouTube + transcript
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extract the key claims from https://example.com/article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# any URL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't pick the vertical. You don't switch endpoints. You just ask, and you get back a cited answer with structured per-shape fields — rating and address for places, price and store for shopping, flight numbers and segments for flights, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "OpenAI-compatible" matters
&lt;/h2&gt;

&lt;p&gt;The wire format is the standard &lt;code&gt;chat.completions&lt;/code&gt; shape, which means &lt;strong&gt;anything that speaks OpenAI's API speaks pixserp&lt;/strong&gt;. Swap &lt;code&gt;base_url&lt;/code&gt; and you're done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The official &lt;code&gt;openai&lt;/code&gt; Python / JS / Go SDKs work&lt;/li&gt;
&lt;li&gt;LangChain works (use &lt;code&gt;ChatOpenAI&lt;/code&gt; with &lt;code&gt;api_base&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;LlamaIndex works (&lt;code&gt;OpenAILike&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Vercel AI SDK works (&lt;code&gt;createOpenAI&lt;/code&gt; with &lt;code&gt;baseURL&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Cursor, Continue, any tool that lets you set a base URL works&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;curl&lt;/code&gt; works (it's just HTTP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No bespoke client to install. No per-framework adapter to maintain. The same code that talks to GPT-4 talks to pixserp, just pointed at a different host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pixserp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pxs_…&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://pixserp.com/api/v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pixserp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pixserp-fast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Latest CRISPR developments in 2026&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// cited answer prose&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;citations&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// structured sources&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Citations are first-class
&lt;/h2&gt;

&lt;p&gt;Every fact in the answer comes back with both an inline &lt;code&gt;[1]&lt;/code&gt; marker and a structured entry in &lt;code&gt;message.citations&lt;/code&gt;. Each entry has a &lt;code&gt;kind&lt;/code&gt; (&lt;code&gt;web&lt;/code&gt;, &lt;code&gt;news&lt;/code&gt;, &lt;code&gt;place&lt;/code&gt;, &lt;code&gt;shopping&lt;/code&gt;, &lt;code&gt;flight&lt;/code&gt;, &lt;code&gt;hotel&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;, &lt;code&gt;transcript&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;webpage&lt;/code&gt;) plus per-kind structured fields. UI rendering becomes "render the cards", not "parse free-form prose":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="s2"&gt;"place"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"Ippudo NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"65 4th Ave, New York, NY 10003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.google.com/maps/place/…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"markdown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**Ippudo NY** — 4.5★ · 65 4th Ave · New York"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop into a card component, you have a working place card without writing a single regex over markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing — flat per request
&lt;/h2&gt;

&lt;p&gt;This is where we differ most from the rest of the category.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Use for&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pixserp-fast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quick lookups, chat-style&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1.50 / 1k&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pixserp-standard&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Balanced research&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$2.50 / 1k&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pixserp-deep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-angle thorough&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$3.50 / 1k&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pixserp-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-step research loop (up to 100 steps)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.0035 / step&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For comparison, May 2026 list prices on the rest of the category:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exa&lt;/strong&gt;: $7–$15 / 1k + extra $1 / 1k per content-type fetch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tavily&lt;/strong&gt;: $8–$16 / 1k pay-as-you-go&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perplexity Sonar&lt;/strong&gt;: $1–$15 per 1M tokens + an extra $14–$22 / 1k on Pro Search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brave Summarizer&lt;/strong&gt;: async polling, multi-second floor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're cheaper than Exa by ~4×, cheaper than Tavily by ~5×, and predictable in a way per-token APIs aren't. &lt;strong&gt;No per-token roulette, no metered sub-calls to count, no end-of-month surprises.&lt;/strong&gt; You know the cost before you call.&lt;/p&gt;

&lt;p&gt;How? Because we have one variable (request count) instead of N (tokens × pages × searches × verticals). The variance averages out at scale; we price the average plus a margin and offer it back as a fixed unit. The teams we talked to before launching this pricing all had the same story: &lt;em&gt;"month one was fine, month three the bill was 4× projected because traffic shifted toward harder questions."&lt;/em&gt; Flat pricing ends that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming, JSON schema, MCP
&lt;/h2&gt;

&lt;p&gt;A few other things that ship by default:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSE streaming&lt;/strong&gt; — same OpenAI wire format. &lt;code&gt;stream: true&lt;/code&gt; and you get token-by-token chunks. Time-to-first-token on &lt;code&gt;pixserp-fast&lt;/code&gt; is ~1 second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON schema outputs&lt;/strong&gt; — pass &lt;code&gt;response_format&lt;/code&gt; with a schema, get JSON back with web-grounded values. No parsing, no validation gymnastics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pixserp-fast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Top 3 aerospace companies, CEO, founded year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;response_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;companies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;companies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;array&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ceo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;founded_year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ceo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;founded_year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;companies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;—&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ceo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;—&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;founded_year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MCP server&lt;/strong&gt; — we also ship a dedicated &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; server so any MCP-compatible client (Claude Desktop, Cursor, Zed, Claude Code, Cline, Continue) can pick up pixserp as a &lt;code&gt;search&lt;/code&gt; tool with one config block. Full install configs are at &lt;a href="https://pixserp.com/mcp" rel="noopener noreferrer"&gt;pixserp.com/mcp&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it deletes from your codebase
&lt;/h2&gt;

&lt;p&gt;If you're coming from a stitched-together setup (SerpAPI + scraper + your own LLM synthesis), what you can delete:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Result-picking logic&lt;/strong&gt; — pixserp picks which URLs to fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page scraper&lt;/strong&gt; (Playwright / BeautifulSoup / trafilatura) — content extraction is server-side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML cleaner / boilerplate stripper&lt;/strong&gt; — same.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token-budget truncator&lt;/strong&gt; — pixserp returns a final answer, not raw context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Citation-marker injector&lt;/strong&gt; — built into the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The second LLM call for synthesis&lt;/strong&gt; — pixserp &lt;em&gt;is&lt;/em&gt; the synthesis. You delete the OpenAI bill underneath.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-vertical clients&lt;/strong&gt; (flight API, hotels API, shopping API, YouTube transcript service) — all behind the same endpoint now.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If that stack is ~300 lines and three separate vendor accounts, you delete ~300 lines and consolidate three accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest comparison with the rest of the category
&lt;/h2&gt;

&lt;p&gt;We're hardly the only AI search API. If you're evaluating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exa&lt;/strong&gt; is the right pick if you specifically want embedding-similarity over papers/blogs and synthesis isn't part of the job. Their &lt;code&gt;/contents&lt;/code&gt; endpoint is solid for "find me posts similar to X".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tavily&lt;/strong&gt; is the most pragmatic for plain web/news Q&amp;amp;A if you don't need streaming, structured outputs, or any verticals beyond web/news.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perplexity Sonar&lt;/strong&gt; is the right pick if you want their consumer-product research engine specifically, and you're OK with per-token pricing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brave Search&lt;/strong&gt; is great if you only need raw SERP results and want a simple per-1k web/news/images call. No native synthesis, no agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;pixserp&lt;/strong&gt; is the pick if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;strong&gt;one endpoint&lt;/strong&gt; that handles web + news + places + shopping + flights + hotels + YouTube + transcripts + any URL — without integrating four separate APIs.&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;flat per-request pricing&lt;/strong&gt; so finance can model your bill.&lt;/li&gt;
&lt;li&gt;You want a &lt;strong&gt;drop-in OpenAI-compatible&lt;/strong&gt; wire format so existing code works.&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;structured citations&lt;/strong&gt; with per-shape fields, not just URL lists.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;If you want to try it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://pixserp.com/api-keys" rel="noopener noreferrer"&gt;Get an API key&lt;/a&gt;&lt;/strong&gt; — $2.50 free credit on signup, no card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Point your &lt;code&gt;base_url&lt;/code&gt;&lt;/strong&gt; at &lt;code&gt;https://pixserp.com/api/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick a model&lt;/strong&gt; — start with &lt;code&gt;pixserp-fast&lt;/code&gt; for chat, &lt;code&gt;pixserp-standard&lt;/code&gt; for research.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship.&lt;/strong&gt; Your existing &lt;code&gt;chat.completions.create()&lt;/code&gt; code keeps working.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Docs and per-language quickstarts: &lt;a href="https://pixserp.com/docs" rel="noopener noreferrer"&gt;pixserp.com/docs&lt;/a&gt;. MCP install: &lt;a href="https://pixserp.com/mcp" rel="noopener noreferrer"&gt;pixserp.com/mcp&lt;/a&gt;. Playground (in-browser, no setup): &lt;a href="https://pixserp.com/playground" rel="noopener noreferrer"&gt;pixserp.com/playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We run &lt;a href="https://teti.ai" rel="noopener noreferrer"&gt;Teti AI&lt;/a&gt; on this exact stack. If pixserp goes down, Teti goes down too — so the uptime expectations are the same as our own product. Drop it in and tell us what you build.&lt;/p&gt;

&lt;p&gt;— The Teti AI team&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>llm</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
