Skip to the guide
Scholaris

API v1 https://scholaris-v2.jlsf2005.workers.dev/api/v1

Upload anything, search, ask and cite.

One header, nine verbs, JSON in and out (or Markdown, which agents read better). Every passage comes with its exact printed page or second and a link to the reader, and no citation comes from a model: they all come from the stored anchor.

Works the same in the cloud and in the home version.

Start in a minute

  1. Create a key in Ajustes (Settings) → Claves de API (API keys), with the read and write scopes. It is shown only once: keep it. Create key
  2. Upload a file. The request waits until it has been read (up to a minute; if it takes longer, it answers 202 with where to ask).
  3. Search. Every passage brings a citation ready to paste, the exact locator and the link to the reader at that page.
export SCHOLARIS=sch_…   # tu clave

curl -s https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos -H "Authorization: Bearer $SCHOLARIS" \
  -H "Content-Type: application/pdf" --data-binary @articulo.pdf

curl -sG https://scholaris-v2.jlsf2005.workers.dev/api/v1/buscar -H "Authorization: Bearer $SCHOLARIS" \
  --data-urlencode "q=atención escalada" -d k=3 -d formato=markdown

The examples can be copied and pasted as they are: you only need the SCHOLARIS variable with your key.

The nine verbs

Everything hangs from one base address. Field names are in Spanish and lower case; this guide translates them.

https://scholaris-v2.jlsf2005.workers.dev/api/v1

POST /api/v1/documentos

Upload anything: the file as the body (PDF, EPUB, DOCX, slides, spreadsheets, audio, video, images), a multipart form with the field archivo (file), or JSON with url (a web page, a PDF, YouTube, Vimeo, a podcast). If the file was already there (same SHA-256), you get the existing one with duplicado (duplicate).

# Un fichero, como cuerpo (con su tipo y su nombre)
curl -s "https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos?nombre=libro.epub" -H "Authorization: Bearer $SCHOLARIS" \
  -H "Content-Type: application/epub+zip" --data-binary @libro.epub

# Una URL (web, PDF, YouTube, Vimeo, pódcast), sin esperar
curl -s "https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos?esperar=0" -H "Authorization: Bearer $SCHOLARIS" \
  -H "Content-Type: application/json" -d '{"url": "https://www.youtube.com/watch?v=fNk_zzaMoSs"}'

# Un formulario, con la ficha
curl -s https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos -H "Authorization: Bearer $SCHOLARIS" \
  -F archivo=@entrevista.mp3 -F titulo="A fondo" -F autores="Cortázar, Julio" -F anio=1977

GET /api/v1/documentos

List the library. Filter with q (title or author) and estado (status); page with cursor.

curl -sG https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos -H "Authorization: Bearer $SCHOLARIS" -d estado=listo -d formato=markdown

GET /api/v1/documentos/{id}

The record: title, authors, year, status, progress while it is processed and the full reference when it is ready. With esperar=30 (wait) it long-polls until done.

curl -s "https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos/ID?esperar=30" -H "Authorization: Bearer $SCHOLARIS"

DELETE /api/v1/documentos/{id}

Delete it with everything derived from it (pages, vectors, images).

curl -s -X DELETE https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos/ID -H "Authorization: Bearer $SCHOLARIS"

GET /api/v1/documentos/{id}/texto

Read the text by pages or by a time range. desde (from) and hasta (to) take the printed page (23, xiv), the physical position in brackets ([12]) and, in audio and video, times (1:06:56).

# Las páginas 23 a 25 tal como están impresas
curl -sG https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos/ID/texto -H "Authorization: Bearer $SCHOLARIS" -d desde=23 -d hasta=25 -d formato=markdown

# Un tramo de una entrevista
curl -sG https://scholaris-v2.jlsf2005.workers.dev/api/v1/documentos/ID/texto -H "Authorization: Bearer $SCHOLARIS" -d desde=1:06:00 -d hasta=1:08:00

GET /api/v1/buscar

Search passages (hybrid search, reranked). Quote a phrase for a literal match. Each passage brings cita (citation), localizador (locator), ancla (anchor), enlace (link) and the literal texto.

curl -sG https://scholaris-v2.jlsf2005.workers.dev/api/v1/buscar -H "Authorization: Bearer $SCHOLARIS" \
  --data-urlencode "q=la música me metía en el tiempo" -d k=5

POST /api/v1/preguntar

Answer in Markdown with footnotes [^n]. The writer may only cite the passages it is given, and every note is checked. With stream it arrives as events.

curl -s https://scholaris-v2.jlsf2005.workers.dev/api/v1/preguntar -H "Authorization: Bearer $SCHOLARIS" -H "Content-Type: application/json" \
  -d '{"pregunta": "¿Qué le pasa a Johnny con el tiempo cuando toca?"}'

POST /api/v1/citar

Your text back with the citations inserted (exact page, any CSL style) and the bibliography. It also takes a .docx and returns it cited, formatting intact.

curl -s https://scholaris-v2.jlsf2005.workers.dev/api/v1/citar -H "Authorization: Bearer $SCHOLARIS" -H "Content-Type: application/json" \
  -d '{"texto": "La música no saca a Johnny del tiempo: lo mete en otro.", "estilo": "chicago-author-date"}'

# Un .docx, devuelto citado
curl -s "https://scholaris-v2.jlsf2005.workers.dev/api/v1/citar?estilo=apa" -H "Authorization: Bearer $SCHOLARIS" \
  -H "Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document" \
  -H "Accept: application/vnd.openxmlformats-officedocument.wordprocessingml.document" \
  --data-binary @trabajo.docx -o trabajo-citado.docx

POST /api/v1/verificar

Whether your library supports a claim: respaldada (supported), parcial, sin_respaldo (unsupported) or contradicha (contradicted), with the probability and the passages.

curl -s https://scholaris-v2.jlsf2005.workers.dev/api/v1/verificar -H "Authorization: Bearer $SCHOLARIS" -H "Content-Type: application/json" \
  -d '{"afirmacion": "Johnny Carter dice que la música lo mete en el tiempo."}'

Every read accepts formato=markdown (or the header Accept: text/markdown).

What takes time (uploading and citing) waits by default and answers with the result. Not to wait, esperar=0 or the header Prefer: respond-async: the answer is a 202 with progreso_url.

A real session

Five commands recorded as they ran against the home version, with the benchmark files: a short story by Cortázar in PDF and a lecture in audio already uploaded. Long answers are cut where it says […].

export B=http://localhost:8795/api/v1 SCHOLARIS=sch_…
  1. Upload a PDF and wait until it is read

    curl -s "$B/documentos?esperar=120" \
      -H "Authorization: Bearer $SCHOLARIS" \
      -H "Content-Type: application/pdf" \
      --data-binary @cortazar1959perseguidor.pdf \
      | jq '{id, titulo, autores, estado, unidades, referencia}'
    {
      "id": "dmuwzohwaoqul34wy",
      "titulo": "El perseguidor",
      "autores": [
        "Cortázar, Julio"
      ],
      "estado": "listo",
      "unidades": 37,
      "referencia": "Cortázar, J. (1996). El perseguidor."
    }
  2. Search, in Markdown

    curl -sG "$B/buscar" \
      -H "Authorization: Bearer $SCHOLARIS" \
      --data-urlencode "q=la música me metía en el tiempo" -d k=2 -d formato=markdown
    # la música me metía en el tiempo
    
    ## 1. (Cortázar, 1996, p. 5)
    
    *El perseguidor* · p. 5 · [abrir](http://localhost:8795/lector/dmuwzohwaoqul34wy?u=5&f=dmuwzohwaoqul34wy%3At0.8) · `dmuwzohwaoqul34wy:t0.8`
    
    > Vaya si lo he oído; vaya si he tratado de escribirlo bien y verídicamente en mi biografía de Johnny.
    > 
    > —Por eso en casa el tiempo no acababa nunca, sabes. De pelea en pelea, casi sin comer. Y para colmo la religión, ah, eso no te lo puedes imaginar. Cuando el maestro me consiguió un saxo que te hubieras muerto de risa si lo ves, entonces creo que me di cuenta en seguida. La música me sacaba del tiempo, aunque no es más que una manera de decirlo. Si quieres saber lo que realmente siento, yo creo que la música me metía en el tiempo. Pero entonces hay que creer que este tiempo no tiene nada que ver con... bueno, con nosotros, por decirlo así.
    >
    […]
  3. An audio passage: the locator is the second

    curl -sG "$B/buscar" \
      -H "Authorization: Bearer $SCHOLARIS" \
      --data-urlencode "q=vectors as arrows in space" -d k=1 \
      | jq '.pasajes[0] | {cita, localizador, enlace}'
    {
      "cita": "(3Blue1Brown, s. f., 0:11)",
      "localizador": "0:11",
      "enlace": "http://localhost:8795/lector/dmuwzm8ascxmk4ayv?t=11.3&f=dmuwzm8ascxmk4ayv%3Ac9tp39p"
    }
  4. Ask

    curl -s "$B/preguntar?formato=markdown" \
      -H "Authorization: Bearer $SCHOLARIS" \
      -H "Content-Type: application/json" \
      -d '{"pregunta":"¿Qué le pasa a Johnny con el tiempo cuando toca?"}'
    Cuando Johnny toca, experimenta que el tiempo cambia[^1][^2]. Aunque en un principio expresa que «la música me sacaba del tiempo», matiza que en realidad siente que lo «metía en el tiempo», entendiendo que se trata de un tiempo distinto que no tiene que ver con el orden cotidiano[^3]. Niega que se trate de una abstracción; más bien describe que simplemente cambia de lugar, comparando la experiencia con entrar en «un ascensor de tiempo» donde transcurren decenas de pisos en lo que dura una sola frase, mientras asuntos como las hipotecas o la religión dejan de existir para él[^4]. Asimismo, percibe la elasticidad del tiempo mediante la metáfora de una valija en la que, al tocar, es capaz de meter «cientos y cientos de trajes» o «una tienda entera»[^5].
    
    Durante esos momentos musicales, Johnny siente que se abre una puerta y que vuela hacia un estado de seguridad donde queda «sobre todo sin tiempo, sin que después... sin que hubiera después... Por un rato no hubo más que siempre...»[^6]. Sin embargo, esa vivencia dura solo mientras se encuentra sumergido en la música; al dejar de tocar, cae de nuevo «de cabeza» en sí mismo[^6]. Bruno observa que en su ejecución Johnny «siempre está tocando mañana», adelantándose sin esfuerzo al hoy[^3], y viviendo intensamente lo que describe como su «cuarto de hora de minuto y medio», una alteración temporal frente al límite impuesto por los relojes[^1][^7].
    
    [^1]: Cortázar, *El perseguidor* (1996), p. 9.
    [^2]: Cortázar, *El perseguidor* (1996), p. 5.
    [^3]: Cortázar, *El perseguidor* (1996), p. 5.
    [^4]: Cortázar, *El perseguidor* (1996), pp. 5-6.
    [^5]: Cortázar, *El perseguidor* (1996), p. 6.
    [^6]: Cortázar, *El perseguidor* (1996), p. 34.
    [^7]: Cortázar, *El perseguidor* (1996), pp. 16-17.
    
    ---
    
    Fuentes (confianza alta):
    […]
  5. Cite your own text

    curl -s "$B/citar?formato=markdown" \
      -H "Authorization: Bearer $SCHOLARIS" \
      -H "Content-Type: application/json" \
      -d '{"texto":"Johnny Carter siente que la música no lo saca del tiempo, sino que lo mete en otro. Ese tiempo no se parece al de los relojes.","estilo":"apa"}'
    Johnny Carter siente que la música no lo saca del tiempo, sino que lo mete en otro (Cortázar, 1996, p. 5). Ese tiempo no se parece al de los relojes (Cortázar, 1996, pp. 8-9).
    
    ## Referencias
    
    - Cortázar, J. (1996). El perseguidor.

From JavaScript

With fetch and nothing else (Node 20 or the browser). Ten lines: upload, search and ask.

import { readFile } from 'node:fs/promises';
const B = 'https://scholaris-v2.jlsf2005.workers.dev/api/v1';
const h = { Authorization: `Bearer ${process.env.SCHOLARIS}` };
const pedir = async (ruta, o = {}) => (await fetch(B + ruta, { ...o, headers: { ...h, ...o.headers } })).json();

const doc = await pedir('/documentos?nombre=articulo.pdf', { method: 'POST', headers: { 'Content-Type': 'application/pdf' }, body: await readFile('articulo.pdf') });
const { pasajes } = await pedir(`/buscar?k=3&q=${encodeURIComponent('atención escalada')}`);
for (const p of pasajes) console.log(p.cita, p.texto.slice(0, 80), p.enlace);
const r = await pedir('/preguntar', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ pregunta: '¿Qué es la atención multicabeza?' }) });
console.log(r.respuesta);

From Python

The SDK has a five-verb facade over this same API. Install it with pip (it only needs requests):

pip install scholaris-sdk
from scholaris.api import Scholaris

s = Scholaris("sch_…")                      # o la variable SCHOLARIS_CLAVE
doc = s.subir("articulo.pdf")                # también una URL
for p in s.buscar("atención escalada", k=3):
    print(p["cita"], p["texto"][:80], p["enlace"])
print(s.preguntar("¿Qué es la atención multicabeza?")["respuesta"])
print(s.citar("La atención sustituye a la recurrencia.")["texto"])
print(s.verificar("El Transformer prescinde de la recurrencia.")["veredicto"])

For agents

There are two ways in: the MCP server, for agents that speak MCP, and this API, which any agent with an HTTP tool can use.

In Claude Code, with a key that has the «mcp» scope:

claude mcp add --transport http scholaris https://scholaris-v2.jlsf2005.workers.dev/mcp \
  --header "Authorization: Bearer sch_…"

In Claude (web or desktop): Settings → Connectors → Add custom connector, with this address. You sign in to Scholaris and grant access; no key needed.

https://scholaris-v2.jlsf2005.workers.dev/mcp

In Cursor, Windsurf and other MCP clients:

{
  "mcpServers": {
    "scholaris": {
      "url": "https://scholaris-v2.jlsf2005.workers.dev/mcp",
      "headers": { "Authorization": "Bearer sch_…" }
    }
  }
}

An agent that can only read the web finds its instructions in /llms.txt: the verbs, the fields and the rules to cite without inventing.

curl -s https://scholaris-v2.jlsf2005.workers.dev/llms.txt
  1. Cite only what the API returns, and copy cita and localizador verbatim.
  2. When you quote, copy the literal texto; when you paraphrase, still attach the citation.
  3. Give the enlace: it is how the reader checks the page or the second.
  4. If the search finds nothing, say so; do not fill the gap from memory.

When something fails

Errors say what happened in Spanish («mensaje») and in English («message»), with a stable code for your program and a link to this guide:

{
  "error": {
    "codigo": "prohibido",
    "mensaje": "Esta clave de API es de solo lectura.",
    "message": "This key is not allowed to do that (check its scopes: lectura, escritura, mcp).",
    "estado": 403,
    "documentacion": "https://scholaris-v2.jlsf2005.workers.dev/api#errores"
  }
}
CodeHTTPMeaning
no_autenticado401The key is missing, wrong or revoked.
prohibido403The key lacks the scope (writing needs escritura).
peticion_invalida400A field is missing or invalid: the message says which.
no_encontrado404It does not exist or it is not yours.
conflicto409It clashes with the current state; for example, the document has no text yet.
demasiado_grande413This endpoint takes files up to 95 MB; for more, the SDK uploads in parts.
cuota_superada · requiere_pro402A quota of the plan is used up.
limite_de_ritmo429Too many requests: wait the seconds in Retry-After.
proveedor_fallo502An AI provider failed: try again.

Rate limits and quotas are those of your plan, the same as in the app.

To retry without duplicating, send an Idempotency-Key header when uploading and citing: with the same key, for 24 hours, you get the same resource back.

Where every citation comes from

When it reads a document, Scholaris stores an anchor for every passage: the physical page and the printed folio you see on paper, the second of an audio or a video, the slide, the section and paragraph of a web page. That is the ancla field.

The cita and the localizador are written from that anchor, never from what a model says; the enlace opens the reader at that same page or second. If a passage is not in your library, the API will not cite it.