Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Irmu Com Chat GPT API
version: 1.0.0
description: 'Operations tagged ChatGPT across 2 of this provider''s published API definitions: app.irmu.com.yaml, irmu-com-openapi.yaml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://app.irmu.com/api
description: Production
tags:
- name: Chat GPT
description: Putting a question to ChatGPT through the same crawler.
paths:
/chatgpt:
get:
tags:
- Chat GPT
summary: Ask ChatGPT
operationId: chatgpt
description: 'Puts a question to ChatGPT and returns the answer as structured JSON.
The crawler does the asking: it opens ChatGPT, types the prompt into the
composer, waits for the streamed answer and converts it into nodes --
paragraphs, headings, lists, tables, product lists, each keeping the
citations ChatGPT showed. There is no API key of yours involved anywhere,
and no model of ours: this drives the website.
**30 credits, flat.** Unlike `/crawl`, nothing changes that -- `premium`,
`country` and `city` still choose where the crawl exits, and for this
target that may well decide whether it is let in at all, but they do not
move the price. What this costs is a browser held open for up to two
minutes rather than a page''s worth of bytes.
Always JSON, never `text/html`. There is nothing to branch on here.
The key goes in the `Authorization` header, or in `?key=` as on `/crawl`.'
security:
- apiKeyHeader: []
- apiKeyQuery: []
parameters:
- name: prompt
in: query
required: true
description: 'The question, up to 1000 characters.
The limit is not arbitrary. The prompt is typed one character at a
time, so a thousand characters is already half a minute of typing
before ChatGPT starts to think -- against a crawler that waits about
two and a half minutes for the whole job.
'
schema:
type: string
maxLength: 1000
example: elon musk
- name: premium
in: query
required: false
description: 'Ask through the premium proxy pool rather than the standard one. **On
by default**, because the standard pool is the one ChatGPT turns away;
send `false` to use the standard pool. Free either way, unlike on
`/crawl` -- the price is flat.
Sent as text in a query string, so the spellings a person would reach
for all work: `true`, `false`, `1`, `0`, `yes`, `no`, `on`, `off`, in
any case. Anything that is not a yes or a no is refused rather than
quietly read as `false`.
'
schema:
type: string
default: 'true'
example: 'true'
- name: country
in: query
required: false
description: 'The country to appear to be asking from, as a two-letter ISO 3166-1
alpha-2 code. Free, and it changes what ChatGPT answers as much as it
changes anything -- a question about prices asked from Germany is not
the same question.
Required to use `city`.
'
schema:
type: string
minLength: 2
maxLength: 2
pattern: ^[A-Za-z]{2}$
example: de
- name: city
in: query
required: false
description: 'The city inside that country, as the pool spells it. **Requires
`country`.** Free here, where on `/crawl` it doubles the price.
Letters, digits, hyphens and underscores only; a multi-word city is
written `new_york`.
'
schema:
type: string
pattern: ^[A-Za-z][A-Za-z0-9_-]{0,63}$
example: berlin
- name: key
in: query
required: false
description: 'The API key, as an alternative to the `Authorization` header. Ignored
when a header is present. A key in a url is copied into access logs,
proxy logs and `Referer` headers, which is a fair trade for a
machine-to-machine endpoint and a bad one for anything else.
'
schema:
type: string
responses:
'200':
description: 'ChatGPT answered. `conversation` holds the exchange: your prompt, then
the answer as a list of nodes.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ChatGptResult'
example:
prompt: elon musk
conversation:
- role: user
message: elon musk
- role: assistant
response:
- type: paragraph
text: Elon Musk is a businessman known for Tesla and SpaceX.
sources:
- attribution: Reuters
url: https://example.com/article
- type: heading
level: 2
text: Companies
- type: bullet_list
items:
- text: Tesla
- text: SpaceX
credits_charged: 30
credits_remaining: 970
job_id: 0198a9b6-1f4c-7c3e-9a2b-5d8e6f0a1b23_0198a9b6-2a71-7f0d-8c44-9e1b3c5d7f02
status: done
position: null
error: null
'202':
description: 'Paid for and still going. A long prompt can outlast the crawler''s
patience for waiting, though not its patience for working.
Collect it from `/crawl/{job}` -- the same endpoint a slow crawl is
collected from, and free in the same way. One difference worth knowing:
that endpoint answers the crawl envelope, so the answer arrives under
`data.conversation` rather than lifted into `conversation` the way
it is here.
With `debuga=1`, a prompt that is still running also carries `debug`: the url, a
viewport screenshot and the page source as they stood when the
crawler stopped waiting, so a slow prompt shows the chat as it was
rather than nothing at all. Null once it finishes -- the tab is gone
by then. It costs nothing.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ChatGptResult'
example:
prompt: elon musk
conversation: []
credits_charged: 30
credits_remaining: 970
job_id: 0198a9b6-1f4c-7c3e-9a2b-5d8e6f0a1b23_0198a9b6-2a71-7f0d-8c44-9e1b3c5d7f02
status: running
position: null
error: null
debug:
url: https://chatgpt.com/
screenshot: /9j/4AAQSkZJRgABAQ...
html: <!doctype html>...
'401':
description: 'No key, an unknown key, or a caller that is not an organization.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: This endpoint is reached with an organization API key.
'402':
description: 'Not enough credits, or the daily limit is reached. Nothing is charged
and no attempt is recorded.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: 'A malformed request -- no prompt, a prompt over the limit, a city with
no country, or a flag that is not a yes or a no.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
'500':
description: 'Nothing came back. Either the crawl failed outright, or ChatGPT did not
answer within its timeout -- which reads as an empty `conversation` and
an `error` saying so.
**The credits are refunded either way**, so `credits_charged` is `0`.
The attempt is still recorded against `chatgpt.com`, so a customer can
see that we tried.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ChatGptResult'
example:
prompt: elon musk
conversation: []
credits_charged: 0
credits_remaining: 1000
job_id: 0198a9b6-1f4c-7c3e-9a2b-5d8e6f0a1b23_0198a9b6-2a71-7f0d-8c44-9e1b3c5d7f02
status: done
position: null
error: ChatGPT did not answer in time. Nothing was charged.
servers:
- url: https://app.irmu.com/api
description: Production
components:
schemas:
CrawlSnapshot:
type: object
description: 'Where a crawl had got to, for one that is still running or failed. Only
sent when the request carries `debuga=1`.
The crawler stops waiting long before it stops working, and when it stops
waiting it looks at the tab: the url the browser is on, a picture of what
is on screen, and the page source as it stands. That is what this is --
a diagnosis for a request that timed out, not a result. Nothing is
charged for it.
Null on anything that is not still running. A finished or failed crawl has
no tab left to look at, and a crawl still in the queue has not opened one
yet.
Two things to know when reading one. The image is the **viewport**, not
the whole page, so it is not the picture `/screenshot` sells -- it is a
thumbnail at quality 60, meant to be looked at rather than kept. And
either artefact may be missing: they are captured independently, because a
navigation can destroy one without touching the other, so half a snapshot
is ordinary. The missing half reads as null rather than going absent.
A blank image beside a near-empty `html` is not a broken capture -- it
means the crawl stalled *inside* a navigation, with the old document
already replaced. That is itself the answer.
'
required:
- url
- screenshot
- html
properties:
url:
type:
- string
- 'null'
format: uri
description: 'The url the tab was on, which a redirect may have moved away from the
one you asked for.
'
example: https://shop.example/p/1
screenshot:
type:
- string
- 'null'
format: byte
description: 'A base64 JPEG of the viewport, with no `data:` prefix -- decode it as
it stands. Null when the capture raced a navigation and lost.
'
example: /9j/4AAQSkZJRgABAQ...
html:
type:
- string
- 'null'
description: 'The page source at that moment, half-loaded and all. Null when the
capture raced a navigation and lost.
'
example: <!doctype html>...
Error:
type: object
required:
- message
properties:
message:
type: string
ValidationError:
type: object
required:
- message
- errors
properties:
message:
type: string
errors:
type: object
additionalProperties:
type: array
items:
type: string
ChatGptResult:
type: object
description: 'What `/chatgpt` answers. `conversation` is empty while a prompt is still
running and when it went unanswered; `status` and `error` are what tell
those two apart.
'
required:
- prompt
- conversation
- credits_charged
- credits_remaining
- job_id
- status
- debug
properties:
prompt:
type: string
description: The question, as it was asked.
conversation:
type: array
description: 'The exchange. The user turn carries `message`; the assistant turn
carries `response`, a list of nodes.
'
items:
type: object
properties:
role:
type: string
enum:
- user
- assistant
message:
type: string
description: The prompt. Present on the user turn only.
response:
type: array
description: 'The answer, node by node. `type` says which shape each one is:
`paragraph` and `heading` carry `text` (and `heading` a `level`),
`bullet_list` and `ordered_list` carry `items`, `table` carries
`headers` and `rows`, `product_list` carries `items`, and
`divider` carries nothing. Any node may carry `sources`, the
citations ChatGPT showed beside it.
'
items:
type: object
additionalProperties: true
credits_charged:
type: integer
description: '30 for a prompt that was answered or is still running, 0 for one that
was not.
'
example: 30
credits_remaining:
type: integer
job_id:
type: string
description: 'What this ran under. Collect a `202` from `/crawl/{job}` with it.
'
status:
type: string
enum:
- done
- queued
- running
- failed
position:
type:
- integer
- 'null'
description: Where it sits in the crawler's queue while `queued`.
error:
type:
- string
- 'null'
description: Why nothing came back. Null otherwise.
debug:
description: 'Where the crawl had got to, when the crawler stopped waiting and
looked. Only sent when the request carries `debuga=1` -- a screenshot
and the page source run to a megabyte -- and null otherwise.
'
oneOf:
- $ref: '#/components/schemas/CrawlSnapshot'
- type: 'null'
securitySchemes:
apiKeyHeader:
type: http
scheme: bearer
description: '`Authorization: Bearer <key>`. The usual way in.
'
apiKeyQuery:
type: apiKey
in: query
name: key
description: '`?key=<key>`. Accepted on the crawl route only, and only when no
`Authorization` header was sent.
'
x-refined-from:
- app.irmu.com.yaml
- irmu-com-openapi.yaml