Basics
The Bot API is a plain HTTPS API. Each call is one request to
https://api.kyachat.org/bot<token>/<method>The API uses the conventions most bot frameworks already know: the token in the path, getUpdates or webhooks for incoming updates, and inline keyboards for buttons. Many existing bot libraries work once you set their API base URL to https://api.kyachat.org.
Examples use $TOKEN for your bot’s token. Keep it secret: anyone with the token controls the bot.
Bots and tokens
- Create a bot in KyaChat Web under Settings > Bots: a name and an @username of 5 to 32 letters, digits or underscores, starting with a letter. You can have up to 20 bots, create 10 an hour and rotate 20 tokens an hour.
- A token looks like
<bot id>:<secret>, where the secret is 43 characters of base64url. It is shown once. KyaChat stores only a hash of it, so Settings shows just the first few characters. - Rotate token makes the old token fail straight away. Revoke bot deletes the bot account.
- The token goes in the URL path only. There is no header form.
- A bot is an ordinary account, so people find it by @username, open a private chat or add it to groups and channels. In a group a bot receives every message.
Requests and responses
- Use
GETorPOST. Parameters can come from a JSON body (Content-Type: application/json), a form (application/x-www-form-urlencodedormultipart/form-data) or the query string. In forms,reply_markupis a JSON string. - Success:
{"ok":true,"result":...}. Error:{"ok":false,"error_code":N,"description":"..."}, plus"parameters":{"retry_after":S}and aRetry-Afterheader on 429.
curl -sS "https://api.kyachat.org/bot$TOKEN/getUpdates?offset=0&timeout=0"
{"ok":true,"result":[]}Chat IDs
- Private chat:
chat.idis the person’s user ID, a positive number. A bot can only write to someone who has already messaged it; otherwise the API answers 403. - Group or channel:
chat.idis a negative number. The bot must be a member. Posting in a channel needs admin rights with permission to post, and the post is signed by the channel (sender_chat).
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/sendMessage -H 'Content-Type: application/json' -d '{"chat_id":2,"text":"Hello"}'
{"ok":false,"error_code":403,"description":"Forbidden: bot can't initiate a conversation with a user who has not messaged it first."}Methods
getMe
Returns the bot’s own user. A quick way to check a token.
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/getMe
{"ok":true,"result":{"id":4,"is_bot":true,"first_name":"Match Day Bot","username":"matchday_bot"}}sendMessage
chat_id and text (up to 4,096 characters; **bold**, __italic__ and `code` are shown formatted in the app). Optional reply_to_message_id and reply_markup.inline_keyboard: up to 10 rows of up to 8 buttons. Each button has text (1 to 64 characters) and exactly one of callback_data (1 to 64 bytes) or url (http or https).
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/sendMessage \
-H 'Content-Type: application/json' \
-d '{"chat_id":2,"text":"Hi Ben! Are you playing on **Saturday**?",
"reply_to_message_id":7,
"reply_markup":{"inline_keyboard":[[{"text":"Yes, count me in","callback_data":"rsvp:yes"},
{"text":"Not this week","callback_data":"rsvp:no"}],
[{"text":"Fixtures","url":"https://example.com/fixtures"}]]}}'
{"ok":true,"result":{"message_id":8,"date":1793154897,
"chat":{"id":2,"type":"private","first_name":"Ben Ward","username":"benward"},
"from":{"id":4,"is_bot":true,"first_name":"Match Day Bot","username":"matchday_bot"},
"text":"Hi Ben! Are you playing on **Saturday**?","reply_to_message":{"message_id":7},
"reply_markup":{"inline_keyboard":[...]}}}editMessageText
chat_id, message_id, text and an optional reply_markup. Works on the bot’s own messages for 48 hours. After the edit the buttons are exactly the reply_markup you send: leave it out and the buttons disappear.
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/editMessageText -H 'Content-Type: application/json' \
-d '{"chat_id":2,"message_id":8,"text":"Great, you are in for Saturday"}'
{"ok":true,"result":{"message_id":8,...,"text":"Great, you are in for Saturday"}}deleteMessage
chat_id and message_id. The bot’s own messages, or other people’s where the bot is a group admin or a channel admin with permission to delete.
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/deleteMessage -H 'Content-Type: application/json' -d '{"chat_id":2,"message_id":11}'
{"ok":true,"result":true}sendPhoto and sendDocument
Multipart form with chat_id, a file field photo or document, and optional caption and reply_to_message_id. Photos are converted to WebP (10 MB at most); documents can be up to 25 MB. photo in the result is a single object. Bots cannot download files yet.
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/sendPhoto -F chat_id=2 -F 'photo=@pitch.png;type=image/png' -F 'caption=Pitch 3 tomorrow'
{"ok":true,"result":{"message_id":9,...,"photo":{"file_name":"pitch.webp","mime_type":"image/webp","file_size":12220},"caption":"Pitch 3 tomorrow"}}
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/sendDocument -F chat_id=2 -F 'document=@fixtures.txt;type=text/plain'
{"ok":true,"result":{"message_id":10,...,"document":{"file_name":"fixtures.txt","mime_type":"text/plain","file_size":85}}}getUpdates
offset (the first update_id you want), limit from 1 to 100 (default 100) and timeout from 0 to 25 seconds for long polling.
- An
offsetconfirms every update below it for good: they are not returned again, and a webhook set later does not resend them. Updates are kept for 14 days. - The API answers 409 while a webhook is set, and 409 for a second long poll by the same bot at the same time.
- When the server is busy with other long polls, a poll may answer straight away as if
timeoutwere 0. Simply poll again. - A bot never receives its own messages or system notices.
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/getUpdates -H 'Content-Type: application/json' -d '{"timeout":10}'
{"ok":true,"result":[{"update_id":59,"message":{"message_id":7,"date":1793153398,"chat":{...},
"from":{"id":2,"is_bot":false,"first_name":"Ben Ward","username":"benward"},"text":"/start"}}]}setWebhook and deleteWebhook
url (https only, up to 2,000 characters, no user name or password in it) and an optional secret_token (1 to 256 of A-Z a-z 0-9 _ -).
- Each update is posted as JSON. When you set a secret, every request carries the header
X-KyaChat-Bot-Api-Secret-Token: <secret_token>. Check it before you trust the request. - Updates arrive one at a time per bot, in order. Any 2xx answer counts as delivered. Anything else (other status codes, redirects, which are never followed, timeouts of 8 seconds or 5 seconds to connect, TLS errors) is retried after 5 seconds, then 10, 20, 40 and so on up to 5 minutes, until it succeeds or you delete the webhook. Replies in your webhook’s response body are ignored: call the API instead.
- For safety, webhook addresses that resolve to private, loopback, link-local or other internal networks are refused, both when you set the webhook and before every delivery.
deleteWebhookclears the address and the secret. Pending updates are then available togetUpdates.
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/setWebhook -H 'Content-Type: application/json' \
-d '{"url":"https://bot.example.com/kyachat","secret_token":"my-hook-secret_42"}'
{"ok":true,"result":true}
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/setWebhook -H 'Content-Type: application/json' -d '{"url":"http://bot.example.com/kyachat"}'
{"ok":false,"error_code":422,"description":"The webhook URL must be a plain https:// address."}
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/deleteWebhook
{"ok":true,"result":true}Not available yet
Methods not listed here answer 404, for example answerCallbackQuery. A button stops showing as busy by itself once the tap is delivered; answer it with editMessageText or sendMessage.
curl -sS -X POST https://api.kyachat.org/bot$TOKEN/answerCallbackQuery -d callback_query_id=x
{"ok":false,"error_code":404,"description":"Method not found: answerCallbackQuery"}Update objects
Update {update_id, message | callback_query}
Message {message_id, date, chat, from | sender_chat, text?,
photo? | document? | voice? {file_name, mime_type, file_size},
caption?, reply_to_message? {message_id}, reply_markup? {inline_keyboard}}
Chat private: {id (user id), type: "private", first_name, username}
group or channel: {id (negative), type: "group" | "channel", title, username}
User {id, is_bot, first_name, username}
CallbackQuery {id, from (User), message (Message with its keyboard), data, chat_instance}A button tap, as a webhook body or a getUpdates item:
{"update_id":66,"callback_query":{"id":"a441b176ab16cbe7f70c814d",
"from":{"id":2,"is_bot":false,"first_name":"Ben Ward","username":"benward"},
"message":{"message_id":8,...},"data":"rsvp:yes","chat_instance":"3"}}Errors
| Code | When |
|---|---|
| 400 | The body says JSON but is not valid JSON. |
| 401 | Wrong, revoked or rotated token. |
| 403 | A chat the bot may not write to (nobody messaged it first, not a member, no permission to post, blocked), or a message that is not the bot’s to edit. |
| 404 | Unknown method, or a message that does not exist or the bot cannot see. |
| 409 | getUpdates while a webhook is set, or a second long poll at the same time. |
| 422 | A parameter is missing or not valid. |
| 429 | Too many requests. Wait for retry_after seconds. |
Rate limits
- 120 calls every 10 seconds per bot.
- 30 wrong tokens a minute from one address. After that, every Bot API call from that address gets 429 for 60 seconds.
- People tapping buttons: 30 taps every 30 seconds per person.
curl -sS -i -X POST https://api.kyachat.org/bot$TOKEN/getMe # the 122nd call within 10 seconds
HTTP/1.1 429 Too Many Requests
Retry-After: 2
{"ok":false,"error_code":429,"description":"Too many requests. Slow down.","parameters":{"retry_after":2}}Questions about the Bot API, or a method you need? Contact KyaChat support.