# Science AI Journal API: first call in one minute

Research tools over HTTPS with one header. Base URL: https://scienceaijournal.com. Auth: `Authorization: Bearer saij_…`. Every reply is JSON. Machine-readable description: https://scienceaijournal.com/developers/api/openapi.json (OpenAPI 3.1).

## 1. Get a key

Create an account, open https://scienceaijournal.com/settings?section=api-keys, press "New API key" and copy the saij_… token (it is shown once). Any account can create keys. Put it in the environment:

```sh
export SCIENCE_AI_API_KEY=saij_YOUR_KEY
```

## 2. Make a call

### Prior-publication check (no credits)

`POST /api/prior-publication`. Checks a title (and an abstract, if you send one) for an earlier publication or preprint across the scholarly indexes and our library. Returns: A status (for example preprint_found), a one-line message, the source and a link. Takes up to about 20 seconds.
This endpoint also answers without a key, at lower per-IP limits; with a key the limits are per account.

curl:

```sh
curl -sS -X POST https://scienceaijournal.com/api/prior-publication \
  -H "Authorization: Bearer $SCIENCE_AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Attention Is All You Need"}'
```

Python:

```python
import json, os, urllib.error, urllib.request

key = os.environ["SCIENCE_AI_API_KEY"]
req = urllib.request.Request(
    "https://scienceaijournal.com/api/prior-publication",
    data=json.dumps({"title":"Attention Is All You Need"}).encode(),
    headers={
        "Authorization": f"Bearer {key}",
        "Content-Type": "application/json",
        "User-Agent": "my-research-app/1.0",  # the default Python-urllib agent is refused (403, error code 1010)
    },
    method="POST",
)
try:
    with urllib.request.urlopen(req, timeout=120) as r:
        print(r.status, json.dumps(json.load(r), indent=2)[:2000])
except urllib.error.HTTPError as e:
    print(e.code, e.read().decode()[:2000])  # the body says why
```

JavaScript (Node 18+):

```javascript
const res = await fetch("https://scienceaijournal.com/api/prior-publication", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SCIENCE_AI_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({"title":"Attention Is All You Need"}),
});
console.log(res.status, JSON.stringify(await res.json(), null, 2).slice(0, 2000));
```

Read first in the reply: `status`, `message`, `source`, `link`, `confidence`.

### Literature search (no credits)

`POST /api/research-gaps`. Searches the literature on a topic and returns the field overview and the closest papers. Returns: A field overview (paper counts, top venues, year range) and three paper lists: most cited, most recent and from our library. Takes about 5 to 10 seconds.

curl:

```sh
curl -sS -X POST https://scienceaijournal.com/api/research-gaps \
  -H "Authorization: Bearer $SCIENCE_AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"soil microbial networks under grassland degradation"}'
```

Python:

```python
import json, os, urllib.error, urllib.request

key = os.environ["SCIENCE_AI_API_KEY"]
req = urllib.request.Request(
    "https://scienceaijournal.com/api/research-gaps",
    data=json.dumps({"query":"soil microbial networks under grassland degradation"}).encode(),
    headers={
        "Authorization": f"Bearer {key}",
        "Content-Type": "application/json",
        "User-Agent": "my-research-app/1.0",  # the default Python-urllib agent is refused (403, error code 1010)
    },
    method="POST",
)
try:
    with urllib.request.urlopen(req, timeout=120) as r:
        print(r.status, json.dumps(json.load(r), indent=2)[:2000])
except urllib.error.HTTPError as e:
    print(e.code, e.read().decode()[:2000])  # the body says why
```

JavaScript (Node 18+):

```javascript
const res = await fetch("https://scienceaijournal.com/api/research-gaps", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SCIENCE_AI_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({"query":"soil microbial networks under grassland degradation"}),
});
console.log(res.status, JSON.stringify(await res.json(), null, 2).slice(0, 2000));
```

Read first in the reply: `fieldOverview`, `mostCited`, `mostRecent`, `libraryPapers`, `queryUsed`.

### AI review, one reviewer agent (4 credits per call)

`POST /api/review/claude`. Runs one specialist reviewer agent (here: methodology) on the manuscript text you send. Returns: Strict JSON: a score, a decision, strengths, concerns and questions for the authors. Takes about 20 to 60 seconds.

curl:

```sh
curl -sS -X POST https://scienceaijournal.com/api/review/claude \
  -H "Authorization: Bearer $SCIENCE_AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agentType":"methodology","prompt":"Review the methodology of this manuscript. Title: Soil microbial networks under grassland degradation. Methods: We sampled 36 plots along a degradation gradient, sequenced 16S rRNA amplicons and built co-occurrence networks with SparCC (threshold 0.6); network metrics were compared with a one-way ANOVA."}'
```

Python:

```python
import json, os, urllib.error, urllib.request

key = os.environ["SCIENCE_AI_API_KEY"]
req = urllib.request.Request(
    "https://scienceaijournal.com/api/review/claude",
    data=json.dumps({"agentType":"methodology","prompt":"Review the methodology of this manuscript. Title: Soil microbial networks under grassland degradation. Methods: We sampled 36 plots along a degradation gradient, sequenced 16S rRNA amplicons and built co-occurrence networks with SparCC (threshold 0.6); network metrics were compared with a one-way ANOVA."}).encode(),
    headers={
        "Authorization": f"Bearer {key}",
        "Content-Type": "application/json",
        "User-Agent": "my-research-app/1.0",  # the default Python-urllib agent is refused (403, error code 1010)
    },
    method="POST",
)
try:
    with urllib.request.urlopen(req, timeout=120) as r:
        print(r.status, json.dumps(json.load(r), indent=2)[:2000])
except urllib.error.HTTPError as e:
    print(e.code, e.read().decode()[:2000])  # the body says why
```

JavaScript (Node 18+):

```javascript
const res = await fetch("https://scienceaijournal.com/api/review/claude", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SCIENCE_AI_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({"agentType":"methodology","prompt":"Review the methodology of this manuscript. Title: Soil microbial networks under grassland degradation. Methods: We sampled 36 plots along a degradation gradient, sequenced 16S rRNA amplicons and built co-occurrence networks with SparCC (threshold 0.6); network metrics were compared with a one-way ANOVA."}),
});
console.log(res.status, JSON.stringify(await res.json(), null, 2).slice(0, 2000));
```

Read first in the reply: `score`, `verdict`, `summary`, `strengths`, `concerns`.

## 3. Costs and limits

The prior-publication check and the literature search spend no credits. AI review costs 4 credits per agent call; a new account's 25 welcome credits cover 6 such calls. A refused call is never charged. Limits: 60 calls a minute and 2000 a day per key.

## 4. When a call fails

| Status | What happened | What to do next |
|---|---|---|
| 400 | The body is not JSON or lacks a required field. | Send Content-Type: application/json and the required field (title, query or prompt). Nothing is charged. |
| 401 | No key, a mistyped key, a revoked key, or an endpoint that does not accept API keys yet. | Send "Authorization: Bearer saij_…" with a key from Settings, then API keys. The key starts with saij_ and is 27 characters long. Nothing is charged. |
| 402 | The account behind the key holds fewer credits than the call costs. | The reply names creditsAvailable and creditsNeeded. The prior-publication check and the literature search need no credits; credits for the paid calls are on the pricing page. Nothing is charged. |
| 403 | One of three. A JSON reply naming a scope: an organisation key that does not carry this endpoint's scope. The JSON reply "Automated access to this endpoint is not permitted." from the literature search: the key was not recognised (mistyped or revoked) and your client's User-Agent looks like a script, so the call was treated as an anonymous scraper. Or the plain text "error code: 1010": the network edge refused your HTTP client's default User-Agent (Python urllib, PycURL and libwww-perl are refused; curl, requests, httpx and fetch pass). | For "Automated access", check the key: copy it again from Settings, then API keys (it starts with saij_ and is 27 characters long). For 1010, send your own User-Agent header (any name, for example "my-research-app/1.0"). For a scope refusal, use a key whose scopes include the tool, or a personal key. Nothing is charged. |
| 429 | A rate limit: 60 calls a minute and 2000 a day per key; the prior-publication check allows 10 a minute and 60 an hour per account (5 and 20 per IP address without a key). | Wait the number of seconds in the Retry-After header, then send the same call again. Nothing is charged. |
| 502 | AI review only: the model call failed or the model declined. | That call's credits are refunded ("refunded": true in the reply). Send it again. |
| 504 | AI review only: the model call timed out. | That call's credits are refunded ("refunded": true in the reply). Send it again; a shorter text answers faster. |

## More

- All endpoints, scopes and prices: https://scienceaijournal.com/developers/api
- The same tools inside Claude, Cursor, VS Code or Cline (MCP server): https://scienceaijournal.com/developers/mcp
- OpenAPI 3.1 document: https://scienceaijournal.com/developers/api/openapi.json
