{"id":75,"date":"2026-09-16T23:15:41","date_gmt":"2026-09-16T15:15:41","guid":{"rendered":"https:\/\/wp.qoraapi.com\/ai-api-streaming-sse\/"},"modified":"2026-09-20T03:53:09","modified_gmt":"2026-09-19T19:53:09","slug":"ai-api-streaming-sse","status":"publish","type":"post","link":"https:\/\/qoraapi.com\/blog\/ai-api-streaming-sse\/","title":{"rendered":"AI API Streaming Explained: How SSE Works and How to Consume It"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>AI API streaming<\/strong> is what lets users see the model&#8217;s answer as it is generated rather than waiting for the whole response. The model still produces the same tokens, costs the same amount, and takes the same total time \u2014 what changes is <em>when<\/em> the bytes reach your client. For chat interfaces, autocomplete, and any interactive surface, that single change is often the difference between a product that feels alive and one that feels slow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Under the hood, streaming is just Server-Sent Events over HTTP. The wire format is simple, every major AI provider speaks it, and the SDKs handle the parsing for you. The interesting questions are at the edges: how to handle buffered responses, how to combine streaming with <a href=\"https:\/\/qoraapi.com\/blog\/ai-function-calling-tool-use\/\">function calling<\/a>, what to do when a network connection drops mid-response, and how to make sure <a href=\"https:\/\/qoraapi.com\/blog\/reduce-ai-api-costs\/\">cost and reliability work<\/a> stay intact when bytes arrive one chunk at a time instead of one body. This guide covers all of that, with code that runs unmodified against any <a href=\"https:\/\/qoraapi.com\/blog\/openai-compatible-api-guide\/\">OpenAI-compatible endpoint<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"why-streaming\">Why streaming matters<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The total time to generate an answer is mostly model inference time, and streaming cannot shorten it \u2014 the GPU produces the same tokens at the same rate regardless. What streaming changes is <em>time to first token<\/em> (TTFT): how long the user waits before anything appears on screen. On a 500-token answer that takes four seconds end-to-end, TTFT typically drops from ~4s to ~200ms with streaming, which is the difference between &#8220;the page is broken&#8221; and &#8220;the page is working&#8221;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Three properties of streaming matter in production:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Same cost.<\/strong> You are billed for every token the model produces, delivered or not. Streaming does not reduce spend; it changes the user experience. Our guide to <a href=\"https:\/\/qoraapi.com\/blog\/reduce-ai-api-costs\/\">reducing AI API costs<\/a> covers the patterns that do.<\/li>\n\n<li><strong>Same total time.<\/strong> Inference throughput is what it is. Streaming exposes the same work over more round trips rather than running it faster.<\/li>\n\n<li><strong>Faster perceived latency.<\/strong> This is the only thing that improves \u2014 and for chat, autocomplete, and any &#8220;the user is waiting on this&#8221; surface, it is the only thing that matters.<\/li>\n\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Streaming vs non-streaming at a glance<\/h3>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"has-fixed-layout\"><thead><tr><th>Dimension<\/th><th>Non-streaming<\/th><th>Streaming (SSE)<\/th><\/tr><\/thead><tbody><tr><td>Time to first token<\/td><td>Waits for the entire completion<\/td><td>Tokens arrive as they are generated<\/td><\/tr><tr><td>Perceived latency<\/td><td>High for long answers<\/td><td>Low &mdash; the answer starts appearing immediately<\/td><\/tr><tr><td>Total cost<\/td><td>Same<\/td><td>Same<\/td><\/tr><tr><td>Total wall-clock time<\/td><td>Same<\/td><td>Same<\/td><\/tr><tr><td>Client complexity<\/td><td>One request, one parse<\/td><td>Event loop, partial parsing, reconnection logic<\/td><\/tr><tr><td>Best suited to<\/td><td>Batch jobs, classification, extraction, backend pipelines<\/td><td>Chat UIs, autocomplete, agents &mdash; any interactive surface<\/td><\/tr><tr><td>Failure handling<\/td><td>Retry the whole call<\/td><td>Must handle mid-stream drops and resume cleanly<\/td><\/tr><tr><td>Load-balancer impact<\/td><td>Short-lived connections<\/td><td>Long-lived connections &mdash; check idle timeouts<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-sse-works\">How Server-Sent Events work on the wire<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An AI API streaming response is an HTTP response with <code>Content-Type: text\/event-stream<\/code>. The body is a stream of <em>events<\/em>, each event formatted as one or more <code>field: value<\/code> lines terminated by a blank line. The server flushes events as soon as they are ready, and the client parses them as they arrive.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTP\/1.1 200 OK\nContent-Type: text\/event-stream\nCache-Control: no-cache\nConnection: keep-alive\n\ndata: {\"id\":\"chatcmpl-abc\",\"object\":\"chat.completion.chunk\",\"choices\":[{\"delta\":{\"content\":\"Hel\"},\"index\":0}]}\n\ndata: {\"id\":\"chatcmpl-abc\",\"object\":\"chat.completion.chunk\",\"choices\":[{\"delta\":{\"content\":\"lo\"},\"index\":0}]}\n\ndata: {\"id\":\"chatcmpl-abc\",\"object\":\"chat.completion.chunk\",\"choices\":[{\"delta\":{\"content\":\",\"},\"index\":0}]}\n\ndata: {\"id\":\"chatcmpl-abc\",\"object\":\"chat.completion.chunk\",\"choices\":[{\"delta\":{\"content\":\" world\"},\"index\":0}]}\n\ndata: [DONE]\n\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each <code>data:<\/code> line carries a JSON payload describing one chunk of the answer \u2014 typically a small <code>delta<\/code> containing the new tokens since the last chunk. The final event is <code>data: [DONE]<\/code>, which is the signal that the response is complete. There is no <code>event:<\/code> field for chat completions; some other endpoints (like Assistants) use named event types for state transitions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"consuming-with-sdks\">Consuming streams with the official SDKs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The OpenAI Python and Node SDKs handle SSE for you. You set <code>stream=True<\/code> on the request and iterate over the response \u2014 each iteration gives you a parsed chunk, no manual framing required:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from openai import OpenAI\n\nclient = OpenAI()\n\nstream = client.chat.completions.create(\n    model=\"gpt-4o\",\n    messages=[{\"role\": \"user\", \"content\": \"Write a one-line haiku about streaming.\"}],\n    stream=True,\n)\n\nfull = []\nfor chunk in stream:\n    delta = chunk.choices[0].delta.content\n    if delta:\n        full.append(delta)\n        print(delta, end=\"\", flush=True)\nprint()                              # newline after the stream completes\nprint(\"complete:\", \"\".join(full))<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The same call against an <a href=\"https:\/\/qoraapi.com\/blog\/openai-compatible-api-guide\/\">OpenAI-compatible endpoint<\/a> works unchanged \u2014 just point the client at the relay&#8217;s <code>base_url<\/code>. Under the hood the SDK is doing exactly what you would do by hand: open an HTTP request, read the response line by line, parse each <code>data:<\/code> line as JSON, and yield the parsed object.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"raw-fetch\">Consuming streams with raw fetch<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Sometimes you do not want to pull in an SDK \u2014 for a serverless function, a small worker, or a custom client. Plain <code>fetch<\/code> with a streaming body works just as well. The pattern below runs in any modern JavaScript runtime and is what the SDK is doing internally:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const resp = await fetch(\"https:\/\/qoraapi.com\/v1\/chat\/completions\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${apiKey}`,\n    \"Content-Type\": \"application\/json\",\n  },\n  body: JSON.stringify({\n    model: \"gpt-4o\",\n    messages: [{ role: \"user\", content: \"Stream a one-line haiku.\" }],\n    stream: true,\n  }),\n});\n\nconst reader = resp.body.getReader();\nconst decoder = new TextDecoder();\nlet buffer = \"\";\n\nwhile (true) {\n  const { value, done } = await reader.read();\n  if (done) break;\n\n  buffer += decoder.decode(value, { stream: true });\n\n  \/\/ SSE events are separated by a blank line. Split on \"\\n\\n\"\n  \/\/ and process every complete event in the buffer.\n  let boundary;\n  while ((boundary = buffer.indexOf(\"\\n\\n\")) !== -1) {\n    const event = buffer.slice(0, boundary);\n    buffer = buffer.slice(boundary + 2);\n\n    for (const line of event.split(\"\\n\")) {\n      if (!line.startsWith(\"data:\")) continue;\n      const payload = line.slice(5).trim();\n      if (payload === \"[DONE]\") return;\n      const json = JSON.parse(payload);\n      const delta = json.choices?.[0]?.delta?.content ?? \"\";\n      process.stdout.write(delta);\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The buffer is the key detail: SSE events are framed by blank lines, but a single network read can deliver a partial event, multiple events, or a multi-byte UTF-8 character split across two reads. Code that splits on <code>\"\\n\\n\"<\/code> without a buffer will silently truncate or corrupt the last chunk of every response.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"streaming-with-tools\">Streaming with function calling<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Streaming and <a href=\"https:\/\/qoraapi.com\/blog\/ai-function-calling-tool-use\/\">function calling<\/a> compose, but the way they compose matters. The model&#8217;s tool call is delivered as a complete structured object, not as a token stream \u2014 what streams is the reasoning before the call and the final text after the result. The OpenAI SDK accumulates the tool call into a complete object once the stream ends:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from openai import OpenAI\n\nclient = OpenAI()\n\nstream = client.chat.completions.create(\n    model=\"gpt-4o\",\n    messages=messages,\n    tools=tools,\n    stream=True,\n)\n\n# Accumulate the deltas into a single tool call.\ntool_call_chunks = []\nfor chunk in stream:\n    for delta in chunk.choices[0].delta.tool_calls or []:\n        tool_call_chunks.append(delta)\n\n# Stitch the streamed chunks into a complete tool_call.\nfinal = tool_call_chunks[0]                          # shape stays the same\nfinal.function.arguments = \"\".join(\n    c.function.arguments for c in tool_call_chunks\n)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you want streaming for the surrounding text, this is the right shape: stream the visible content into the UI, accumulate the tool call in the background, and dispatch the function once the stream ends.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pitfalls\">Common pitfalls in production<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most streaming bugs are not about the SDK. They come from the layer between the provider and your client \u2014 and they are worth knowing about before they reach production:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Proxy buffering.<\/strong> Many HTTP proxies (Cloudflare, nginx with <code>proxy_buffering on<\/code>, certain load balancers) buffer the entire response before passing it on. This silently turns streaming into a non-streaming response, and users see a long pause followed by the full answer. The fix is at the proxy: set <code>X-Accel-Buffering: no<\/code> for the streaming path, or use HTTP\/1.1 with <code>Transfer-Encoding: chunked<\/code>.<\/li>\n\n<li><strong>Gzip compression.<\/strong> A gzipped SSE response can look like garbage on a partial read because the gzip header is at the start and the body grows as new chunks arrive. Make sure your client supports <em>streaming decompression<\/em>, not a one-shot inflate.<\/li>\n\n<li><strong>Lost connections.<\/strong> Mobile networks and aggressive proxies close idle HTTP connections. A long generation can exceed the idle timeout, and the client never sees <code>[DONE]<\/code>. Either send keep-alive pings, set a server-side timeout that flushes a chunk periodically, or accept that very long streams need reconnect logic.<\/li>\n\n<li><strong>Truncated JSON.<\/strong> Each <code>data:<\/code> line must be parsed independently. If a read delivers half a JSON payload, do not try to parse it \u2014 wait for the next chunk. Aggregating partial JSON across reads is one of the easiest ways to introduce subtle corruption.<\/li>\n\n<li><strong>Backpressure.<\/strong> If your consumer is slower than the producer, events buffer up in memory and eventually OOM. Real systems either enforce a max-buffer size, drop old events, or pause the producer.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"reliability\">Reliability, retries, and rate limits<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Streaming responses do not play nicely with retries: a connection that drops mid-response has already produced some tokens, and the user has already seen them. Retry policies that assume the request either completed or did not begin will over-charge on streaming. Three rules keep this honest:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Retry on connection errors before any bytes arrive.<\/strong> Once you have displayed a single token to the user, retrying the same request will bill the model twice for the same answer.<\/li>\n\n<li><strong>Do not retry once the stream ends.<\/strong> If the stream completed (you saw <code>[DONE]<\/code> or a finish reason), the request succeeded; a retry is a duplicate.<\/li>\n\n<li><strong>Back off with jitter when the provider throttles.<\/strong> A 429 with <code>retry-after<\/code> still applies to streaming. See <a href=\"https:\/\/qoraapi.com\/blog\/ai-api-rate-limits-429-errors\/\">handling AI API rate limits<\/a> for the broader pattern.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"checklist\">Streaming checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Enable <code>stream: true<\/code> on every interactive surface; leave it off for background jobs.<\/li>\n\n<li>Render tokens to the UI as they arrive, not in one batch at <code>[DONE]<\/code>.<\/li>\n\n<li>Buffer SSE frames across reads \u2014 never assume a network read delivers a complete event.<\/li>\n\n<li>Configure the proxy (Cloudflare, nginx, load balancer) to flush streaming responses immediately.<\/li>\n\n<li>Support streaming decompression if the response is gzipped.<\/li>\n\n<li>Retry only before the first byte has been emitted to the user; never after.<\/li>\n\n<li>Set a <code>max_tokens<\/code> ceiling so a runaway stream cannot bill indefinitely.<\/li>\n\n<li>Surface a clear error when the connection drops before <code>[DONE]<\/code> instead of leaving the UI in limbo.<\/li>\n\n<li>If you combine streaming with <a href=\"https:\/\/qoraapi.com\/blog\/ai-function-calling-tool-use\/\">function calling<\/a>, accumulate the tool call across the stream and dispatch once the model finishes.<\/li>\n\n<li>Watch rate limits and back off with jitter \u2014 streaming responses still count against your quotas.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-cheaper\">Does streaming reduce AI API costs?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. You are billed for every token the model produces regardless of how the response is delivered. Streaming changes <em>when<\/em> the bytes reach you, not how many tokens the model emits. See our <a href=\"https:\/\/qoraapi.com\/blog\/reduce-ai-api-costs\/\">guide to reducing AI API costs<\/a> for the patterns that do.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-faster\">Is streaming faster end-to-end?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. The total time to generate a response is the same \u2014 streaming only shortens the wait before the first token arrives. For a 500-token answer that takes four seconds, total time is still four seconds, but the user sees the first token in roughly 200ms instead of after the full generation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-raw\">Do I need an SDK to consume a stream?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Plain <code>fetch<\/code> with a streaming body works fine \u2014 every modern runtime exposes a <code>ReadableStream<\/code> or equivalent. The SDKs add conveniences like automatic buffering and tool-call accumulation, but the wire protocol is just Server-Sent Events and can be parsed with a few lines of code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-buffering\">Why does my streaming response arrive all at once?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Almost always a buffering proxy. Cloudflare, nginx (with <code>proxy_buffering on<\/code>), and most load balancers buffer the entire response before forwarding it, which silently turns streaming into non-streaming. The fix is at the proxy: <code>X-Accel-Buffering: no<\/code> for nginx, or a streaming-friendly Cloudflare configuration for that path.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-tools\">Can I stream with function calling?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes \u2014 but the tool call itself is delivered as a complete structured object, not as a stream of tokens. What streams is the model&#8217;s reasoning before the call and the final text after. Accumulate the tool-call deltas across the stream and dispatch the function once the stream ends. See our <a href=\"https:\/\/qoraapi.com\/blog\/ai-function-calling-tool-use\/\">function-calling guide<\/a> for the full pattern.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-retry\">Can I retry a streaming request that drops mid-response?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Only if no bytes have reached the user yet. Once the first token is on screen, retrying the same request bills twice for the same answer. The pattern is: retry on connection error before any UI rendering, and surface a &#8220;connection lost&#8221; message after.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-cost\">How do I know how many tokens a stream produced?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Most providers include a final chunk with usage statistics (prompt, completion, total tokens). The OpenAI SDK returns it on the last chunk under <code>chunk.usage<\/code>. Capture it there, log it, and use it for cost tracking \u2014 see our <a href=\"https:\/\/qoraapi.com\/blog\/reduce-ai-api-costs\/\">cost guide<\/a> for how to wire that into a per-task dashboard.<\/li>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"faq-portable\">Is the SSE format the same across providers?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The wire framing (event blocks, <code>data:<\/code> lines, <code>[DONE]<\/code> terminator) is essentially the same \u2014 Server-Sent Events is a standard. The shape of the JSON payload inside each event varies; OpenAI-compatible endpoints use OpenAI&#8217;s shape, others use their own. Our guide to the <a href=\"https:\/\/qoraapi.com\/blog\/openai-compatible-api-guide\/\">OpenAI-compatible API<\/a> explains the wrapper tradeoffs when you call more than one provider.<\/p>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<p class=\"wp-block-paragraph\">Streaming is the smallest change you can make that produces the largest perceived speedup. Enable it on every interactive surface, render tokens as they arrive, and watch for the usual edge cases \u2014 buffering proxies, gzip, lost connections. The cost of streaming is the same as non-streaming, the total time is the same, and your users will think the application is twice as fast. If you want to test it against multiple providers with the same code, create a key at <a href=\"https:\/\/qoraapi.com\/\" target=\"_blank\" rel=\"noopener\">qoraapi.com<\/a> and your existing OpenAI client streams through the relay unchanged.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Related reading<\/h3>\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/qoraapi.com\/blog\/streaming-chat-ui-react\/\">Building a Streaming Chat UI in React: Patterns for SSE Responses<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/build-ai-chatbot-api\/\">How to Build an AI Chatbot with the API<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/load-testing-llm-apps\/\">Load Testing LLM Apps: Throughput, TTFT, and Concurrency<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/multimodal-ai-api\/\">Multimodal AI APIs: Working with Vision and Audio<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/text-to-sql-ai\/\">Text-to-SQL: Letting Users Query Your Database with AI<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/voice-ai-apis\/\">Building Voice AI Apps: TTS, STT, and Realtime APIs<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/vector-database-selection\/\">How to Choose a Vector Database for RAG<\/a><\/li><\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>A practical guide to AI API streaming: how Server-Sent Events work on the wire, how to consume a stream with an SDK or raw fetch, how to combine streaming with function calling, and the buffering pitfalls that catch teams in production.<\/p>\n","protected":false},"author":1,"featured_media":74,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[5,6,9,7,11],"class_list":["post-75","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-api","tag-ai-api","tag-api-gateway","tag-developer-tools","tag-developers","tag-software-development"],"_links":{"self":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts\/75","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/comments?post=75"}],"version-history":[{"count":4,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts\/75\/revisions"}],"predecessor-version":[{"id":254,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts\/75\/revisions\/254"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/media\/74"}],"wp:attachment":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/media?parent=75"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/categories?post=75"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/tags?post=75"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}