logo

ChatGPT Scraper API

Send a prompt to ChatGPT and get the assistant reply, citations, rendered output, and tool data as structured JSON

The ChatGPT API takes a single text prompt and returns the assistant's reply from chatgpt.com as structured JSON. One GET request, no account, no cookies, no browser automation, and no session state to manage on your side.

curl "https://api.scrape.do/plugin/chatgpt/chat?token=$TOKEN&q=ping"

Credit Usage: Each successful request costs 25 credits. For bulk processing, use the Async API with plugins.

Key Features

  • One-shot prompt-to-reply: send a prompt, get the full assistant response back in a single HTTP call.
  • Clean text plus rendered output: get the assistant text with citation markers removed, plus output.markdown and output.html for display.
  • Structured JSON envelope: the response includes the full message document, citations, source links, tool data, model slug, finish reason, and message / conversation IDs.
  • Stateless from your side: every call is independent. No login, no token refresh, no conversation IDs to track.
  • Citation and tool data support: when ChatGPT searches or returns shopping cards, you get flat sources[], tool_data.shopping[], and the literal tool_data.search_queries[] issued upstream.
  • Locale signal: use geoCode to ask ChatGPT to answer in the matching language, such as de for German or tr for Turkish.
  • No render fee: protocol traffic to chatgpt.com is included in the per-call price.

Endpoint

GET https://api.scrape.do/plugin/chatgpt/chat

Basic Example

curl --location --request GET 'https://api.scrape.do/plugin/chatgpt/chat?token=<SDO-token>&q=Explain+how+rainbows+form'
import requests
import json

token = "<SDO-token>"

url = f"https://api.scrape.do/plugin/chatgpt/chat?token={token}&q=Explain+how+rainbows+form"

response = requests.request("GET", url)

print(json.dumps(response.json(), indent=2))
const axios = require('axios');

const token = "<SDO-token>";

const url = `https://api.scrape.do/plugin/chatgpt/chat?token=${token}&q=Explain+how+rainbows+form`;

axios.get(url)
  .then(response => {
    console.log(JSON.stringify(response.data, null, 2));
  })
  .catch(error => {
    console.error(error);
  });
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	token := "<SDO-token>"

	url := fmt.Sprintf(
		"https://api.scrape.do/plugin/chatgpt/chat?token=%s&q=Explain+how+rainbows+form",
		token,
	)

	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(body))
}
require 'net/http'
require 'json'

token = "<SDO-token>"

url = URI("https://api.scrape.do/plugin/chatgpt/chat?token=#{token}&q=Explain+how+rainbows+form")

response = Net::HTTP.get(url)

puts JSON.pretty_generate(JSON.parse(response))
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ChatGPTChat {
    public static void main(String[] args) throws Exception {
        String token = "<SDO-token>";

        String url = String.format(
            "https://api.scrape.do/plugin/chatgpt/chat?token=%s&q=Explain+how+rainbows+form",
            token
        );

        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setRequestMethod("GET");

        BufferedReader reader = new BufferedReader(
            new InputStreamReader(conn.getInputStream())
        );
        String line;
        StringBuilder response = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            response.append(line);
        }
        reader.close();

        System.out.println(response.toString());
    }
}
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string token = "<SDO-token>";

        string url = $"https://api.scrape.do/plugin/chatgpt/chat?token={token}&q=Explain+how+rainbows+form";

        using HttpClient client = new HttpClient();
        string response = await client.GetStringAsync(url);

        Console.WriteLine(response);
    }
}
<?php
$token = "<SDO-token>";

$url = "https://api.scrape.do/plugin/chatgpt/chat?token={$token}&q=Explain+how+rainbows+form";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

echo json_encode(json_decode($response), JSON_PRETTY_PRINT);
?>
curl "https://api.scrape.do/plugin/chatgpt/chat?token=$TOKEN&q=Explain+how+rainbows+form"

Request Parameters

Required

ParameterTypeDescription
tokenstringYour Scrape.do API authentication token
qstringPrompt text to send to ChatGPT. Maximum 1024 characters. URL-encode multi-word prompts

Optional

ParameterTypeDefaultDescription
modelstringautoModel selector passed through to ChatGPT, such as auto, gpt-5, or gpt-4o. Unknown or unavailable values fall back to auto.
geoCodeISO-3166-1 alpha-2usCountry code used as a locale signal. Examples: us for English, de for German, tr for Turkish, br for Brazilian Portuguese, and jp for Japanese. Invalid codes return 400.
raw_sse0 / 10When 1, includes the raw upstream stream body in raw_sse for debugging. include_raw=1 is also accepted.
shopping_placeholders0 / 10When 1, each inline product in output.markdown is replaced with a {{shopping:<id>}} token whose <id> matches a tool_data.shopping[].id. Lets you render your own product cards by ID instead of relying on position. Markdown-only — output.html and data are unaffected. See Render your own product cards.

Example Requests

# Basic prompt
curl "https://api.scrape.do/plugin/chatgpt/chat?q=hello&token=$TOKEN"

# Pin a specific model
curl "https://api.scrape.do/plugin/chatgpt/chat?q=summarize+linux+kernel&model=gpt-5&token=$TOKEN"

# Ask for a German response
curl --get "https://api.scrape.do/plugin/chatgpt/chat" \
  --data-urlencode "q=Was sind die wichtigsten Nachrichten heute" \
  --data-urlencode "geoCode=de" \
  --data-urlencode "token=$TOKEN"

# Include the raw stream for debugging
curl "https://api.scrape.do/plugin/chatgpt/chat?q=hello&raw_sse=1&token=$TOKEN"

# Get inline product placeholders for your own card rendering
curl --get "https://api.scrape.do/plugin/chatgpt/chat" \
  --data-urlencode "q=Show me Nike Pegasus 41 shoes I can buy, with links" \
  --data-urlencode "shopping_placeholders=1" \
  --data-urlencode "token=$TOKEN"

Notes

  • Prompts longer than 1024 characters are rejected before any model call runs, so no credits are spent on them.
  • One call returns one assistant reply. There is no multi-turn conversation state; to follow up, include any prior context inside the new q.
  • geoCode controls the response language through ChatGPT's locale signal. Web-search sources may still be globally mixed because source selection is decided by ChatGPT.

Response

The response includes the full assembled message envelope plus integrator-friendly top-level fields for rendered output, citations, and tool data.

Top-Level Structure

{
  "prompt": "Explain quantum entanglement in one sentence",
  "data": { ... },
  "output": {
    "markdown": "...",
    "html": "..."
  },
  "sources": [
    {
      "url": "...",
      "title": "...",
      "snippet": "...",
      "attribution": "...",
      "pub_date": 1779209196
    }
  ],
  "tool_data": {
    "shopping": [],
    "ads": [],
    "search_queries": []
  },
  "stream_bytes": 18342,
  "upstream_latency": "4.812s",
  "pow_inflight": 0
}
FieldTypeDescription
promptstringEcho of the submitted prompt
dataobjectFull assembled assistant message envelope. data.message.content.parts[0] contains clean assistant text with inline citation markers stripped.
output.markdownstringCleaned reply as Markdown. Citation tokens are rewritten as proper links when the response cited sources.
output.htmlstringoutput.markdown rendered to safe GitHub-flavored HTML.
sourcesarrayFlat, deduplicated citation and web result list. Empty when ChatGPT did not search the web.
tool_data.shoppingarrayProduct entries returned by ChatGPT's shopping tool. Each entry includes a stable id plus title, URL, price, currency, image URL, attribution, and description when available. Use id to match an inline {{shopping:<id>}} token (see shopping_placeholders) back to its product.
tool_data.adsarraySponsored entries when present. Currently reserved and usually empty.
tool_data.search_queriesarrayLiteral web-search query strings ChatGPT issued, in order. Empty when no search fired.
stream_bytesintegerSize of the upstream stream body in bytes.
upstream_latencystringEnd-to-end upstream call duration, such as "4.812s".
raw_ssestringRaw upstream stream body. Only present when raw_sse=1 is set.
pow_inflightintegerCurrent in-flight workload counter. Useful as a load signal when running many parallel requests.

output, sources, and tool_data are always present. For a simple non-web-search answer, sources, tool_data.shopping, tool_data.ads, and tool_data.search_queries are empty arrays.

Useful data Paths

data.message.content.parts[0]                       → clean assistant text
data.message.metadata.content_references[N]         → citations
data.message.metadata.content_references[N].safe_urls
                                                    → resolved citation URLs
data.message.metadata.finish_details                → stop reason
data.message.metadata.model_slug                    → model
data.message.status                                 → "finished_successfully"
data.message.id                                     → message id
data.conversation_id                                → conversation id

Example

{
  "prompt": "Explain how rainbows form",
  "data": {
    "message": {
      "id": "f0a2b1c4-...",
      "status": "finished_successfully",
      "content": {
        "content_type": "text",
        "parts": [
          "Rainbows form when sunlight is refracted, reflected, and dispersed inside water droplets ..."
        ]
      },
      "metadata": {
        "model_slug": "gpt-5",
        "finish_details": { "type": "stop" },
        "content_references": [
          {
            "type": "webpage",
            "title": "How Rainbows Form, NOAA SciJinks",
            "safe_urls": ["https://scijinks.gov/rainbow/"]
          }
        ]
      }
    },
    "conversation_id": "9b8a7c6d-..."
  },
  "output": {
    "markdown": "Rainbows form when sunlight is refracted, reflected, and dispersed inside water droplets ...",
    "html": "<p>Rainbows form when sunlight is refracted, reflected, and dispersed inside water droplets ...</p>\n"
  },
  "sources": [
    {
      "url": "https://scijinks.gov/rainbow/",
      "title": "How Rainbows Form, NOAA SciJinks",
      "snippet": "...",
      "attribution": "NOAA SciJinks",
      "pub_date": 0
    }
  ],
  "tool_data": {
    "shopping": [],
    "ads": [],
    "search_queries": []
  },
  "stream_bytes": 1422,
  "upstream_latency": "2.318s",
  "pow_inflight": 0
}

Message Envelope Fields

FieldTypeDescription
data.message.idstringUnique identifier for this assistant message
data.message.statusstringMessage status. "finished_successfully" for a complete reply
data.message.content.parts[0]stringThe assistant's reply text with inline citation markers removed
data.message.metadata.model_slugstringModel that produced the reply (e.g., "gpt-5")
data.message.metadata.finish_detailsobjectStop reason, e.g., { "type": "stop" }
data.message.metadata.content_referencesarrayInline citations. Absent when the model did not cite anything
data.conversation_idstringIdentifier for the conversation this reply belongs to

content_references[]

FieldTypeDescription
typestringReference type (e.g., "webpage", "image")
titlestringCitation title as shown in the reply
safe_urlsstring[]Resolved URLs for the citation. Present on web citations

Render your own product cards

When ChatGPT returns shopping results, output.markdown renders inline products as links by default. If you render your own product cards, add shopping_placeholders=1 and each inline product becomes a {{shopping:<id>}} token instead:

- {{shopping:36460bc8599e}}
- {{shopping:f6d7a830aa6d}}
- {{shopping:0f7380b3a66f}}

Every <id> matches the id of an entry in tool_data.shopping[]. To render, scan output.markdown for {{shopping:<id>}} tokens and swap each for your own card, looked up by id:

"tool_data": {
  "shopping": [
    {
      "id": "36460bc8599e",
      "title": "Nike Men's Pegasus 41",
      "url": "https://www.nike.com/t/...",
      "price": "$140.00",
      "image_url": "https://...",
      "attribution": "Nike + others"
    }
  ]
}

Things to rely on:

  • Look up by id, not by position. The same product can appear at more than one inline position (you will see the same token repeated), and the order of tool_data.shopping[] does not necessarily follow the order of the text. Counting positions will drift; ID lookup will not.
  • Every token resolves. A token is only emitted when its product has a matching tool_data.shopping[] entry. An inline product with no structured match is left as normal text instead of a token.
  • tool_data.shopping[] can be a superset. Some products may be listed in tool_data.shopping[] without an inline token, because ChatGPT mentioned them in prose. Size your placeholders from the tokens in output.markdown, and use tool_data.shopping[] to fill in card details by id.
  • Markdown-only. This affects output.markdown only. output.html keeps the normal inline product links, and data.message.content.parts[0] is unchanged.

Notes

  • The envelope follows the streaming message shape used by chatgpt.com; we collect the full stream server-side and return the final assembled document.
  • Citation delimiters that appear inline in the raw stream are stripped before the response is returned. The text in parts[0] is clean, citations live separately in metadata.content_references, and sources[] provides a flat URL list for easier integration.
  • A reply that ends with finish_details.type == "stop" is a normal completion. Other values indicate the model stopped early (e.g., a tool call boundary).
  • ChatGPT decides whether to search the web or show shopping results on each call. Empty sources[] or tool_data.shopping[] does not mean the endpoint failed; it means ChatGPT answered without that tool.
  • Prompts like Search the web, Browse the web, or Find live prices are more likely to trigger web or shopping tools than general explanation prompts.
  • Typical responses take 3-15 seconds depending on prompt complexity and response length. The endpoint waits for the assistant message to finish and does not stream partial replies.
  • The per-call timeout is 60 seconds. Shorten unusually long prompts if you receive an upstream timeout.

Error Responses

StatusBodyCause
400{ "error": "token is required" }Missing token parameter
400{ "error": "q (prompt) is required" }Missing or empty q parameter
400{ "error": "q is too long (max 1024 characters)", "message": "..." }Prompt exceeds the 1024-character limit
400{ "error": "unsupported geoCode" }geoCode is not a recognized ISO-3166-1 alpha-2 code
429{ "error": "server busy", "message": "..." }Too many concurrent ChatGPT requests; retry shortly
502{ "error": "request failed", "message": "..." }Upstream call to ChatGPT failed
502{ "error": "empty reply", "message": "..." }Upstream returned an empty assistant message; safe to retry
504{ "error": "upstream timeout", "message": "..." }ChatGPT did not respond within 60 seconds
500{ "error": "internal server error" }Unexpected internal failure

On this page