Joshua has a built-in API: your scripts, a robot, a home-automation box or any app that works with “OpenAI-compatible” servers can send it a question and get an answer back. Every request runs the full Joshua, with web research, tools and memory, on your computer.
http://127.0.0.1:7861/v1.Joshua must be running for the API to answer. The key is stored in ~/AskJoshua/joshua/api.json.
In Terminal, list the models you can use, then ask a question. Replace YOUR-KEY with the key from the panel.
curl http://127.0.0.1:7861/v1/models \ -H "Authorization: Bearer YOUR-KEY"
curl http://127.0.0.1:7861/v1/chat/completions \
-H "Authorization: Bearer YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "joshua",
"messages": [{"role": "user", "content": "What is the weather in Turin today?"}]
}'The answer comes back in the standard format; the text is in choices[0].message.content:
{
"id": "chatcmpl-5f2c9a01",
"object": "chat.completion",
"model": "qwen3-vl:8b-instruct",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "Oggi è domenica, 8 novembre 2026."},
"finish_reason": "stop"
}]
}Joshua may search the web, read pages or run code before answering, so a reply can take from a few seconds to a few minutes, depending on the question and your computer.
The easiest way is the official openai library: point it at Joshua instead of OpenAI.
# pip install openai
from openai import OpenAI
joshua = OpenAI(base_url="http://127.0.0.1:7861/v1", api_key="YOUR-KEY")
reply = joshua.chat.completions.create(
model="joshua",
messages=[{"role": "user", "content": "Summarise today's main news in Italy in three lines."}],
)
print(reply.choices[0].message.content)Or with plain requests, for example on a Raspberry Pi robot that keeps the conversation going by sending the history back each time:
# pip install requests
import requests
URL = "http://192.168.1.20:7861/v1/chat/completions" # your computer's address on the home network
KEY = "YOUR-KEY"
def ask_joshua(text, history=()):
messages = [*history, {"role": "user", "content": text}]
r = requests.post(URL, headers={"Authorization": f"Bearer {KEY}"},
json={"model": "joshua", "messages": messages}, timeout=600)
r.raise_for_status()
return r.json()["choices"][0]["message"]["content"]
print(ask_joshua("Which washing programme for a shirt marked 40 °C, gentle?"))const reply = await fetch("http://127.0.0.1:7861/v1/chat/completions", {
method: "POST",
headers: { "Authorization": "Bearer YOUR-KEY", "Content-Type": "application/json" },
body: JSON.stringify({
model: "joshua",
messages: [{ role: "user", content: "Write a haiku about Turin in the fog." }],
}),
}).then(r => r.json());
console.log(reply.choices[0].message.content);A web page on another site can't call Joshua directly; run this in Node or in your own app.
Send a photo as a data: URL inside the message. With a model that sees images (qwen3-vl does), Joshua looks at it directly; otherwise it uses its local image tool.
import base64
from openai import OpenAI
joshua = OpenAI(base_url="http://127.0.0.1:7861/v1", api_key="YOUR-KEY")
photo = base64.b64encode(open("label.jpg", "rb").read()).decode()
reply = joshua.chat.completions.create(model="joshua", messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What does this care label say?"},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{photo}"}},
],
}])
print(reply.choices[0].message.content)You can give Joshua extra instructions with a system message: they are added after Joshua's own. And you can ask it not to save the exchange as a chat:
reply = joshua.chat.completions.create(
model="joshua",
messages=[
{"role": "system", "content": "You are the voice of a small robot. Answer in one short sentence, in Italian."},
{"role": "user", "content": "Che ore sono?"},
],
extra_body={"joshua_save": False}, # don't keep this exchange as a chat
)| Field | What it does |
|---|---|
| model | joshua = your default local model. Or any name from /v1/models, such as qwen3-vl:8b-instruct. Cloud models (ending in cloud) are used only if you name one. |
| messages | The conversation: system, user and assistant messages, oldest first. Joshua keeps no state between requests, so send the history you want it to remember. |
| stream | true works with clients that expect streaming, but the whole answer arrives in one piece at the end. |
| joshua_save | false = don't save this exchange as a 🔌 chat. Default true. |
By default only programs on the same computer can connect. To use Joshua from a robot, a phone or another computer at home:
~/AskJoshua/stop.sh, then open Joshua again).http://192.168.1.20:7861/v1. Use that address on the other device, with the same key.Keep it to your home network. Don't open port 7861 on your router: the key protects Joshua, but the connection is not encrypted.
| Request | What it does |
|---|---|
| GET /v1/models | Lists joshua and every installed model that can use tools. owned_by says this computer or cloud. |
| POST /v1/chat/completions | Runs Joshua on the messages and returns its answer, in the OpenAI chat format. |
| Error | Meaning |
|---|---|
| 401 | The key is missing or wrong. Send Authorization: Bearer YOUR-KEY. |
| 400 | Something in the request is wrong, such as an unknown model name. The message explains what. |
| connection refused | Joshua isn't running, or (from another device) “Allow other devices” is off or Joshua hasn't been restarted. |
Need only the bare model, without Joshua's tools and memory? Ollama has its own API at http://127.0.0.1:11434, also OpenAI-compatible at /v1, with no key.
joshua_save: false.