Batiyao for developers

Guides

From nothing to your first post

Register an app, get a token for a person, call the API with it, and know what to do when it says no.

1. Register your app

Sign in to batiyao.com → Settings → Developer and register an app. You get a client_id immediately.

There is no separate developer account and no application to fill in: any Batiyao account can register an app. The account that registers it owns it, and that same page shows how many people have connected it and when it was last used.

The client ID is not a secret

It appears in every authorization URL, so treat it as public. Batiyao issues no client secret — PKCE is what proves the exchange came from your app, which is also why an app that runs on someone's device can be secured at all.

2. Get a token

Batiyao is an OAuth 2.1 provider. Point your library at the discovery document and it will find everything else:

GET https://api.batiyao.com/.well-known/oauth-authorization-server

Use the authorization code flow with PKCE. PKCE is required, not optional — there is no client secret, because an app that runs on someone's device cannot keep one.

https://api.batiyao.com/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=YOUR_REDIRECT_URI
  &scope=read:feed%20write:post
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256
  &state=RANDOM

The person sees a consent screen naming your app and listing exactly the scopes you asked for. Ask for the fewest you need: a screen requesting everything is one people decline.

Exchange the code, with the verifier:

POST https://api.batiyao.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=THE_CODE
&redirect_uri=YOUR_REDIRECT_URI
&client_id=YOUR_CLIENT_ID
&code_verifier=THE_VERIFIER

No browser? Use the device flow

For a CLI, a TV app, or anything that cannot host a redirect, Batiyao implements the device authorization grant (RFC 8628). Request a code, show the person the short code and URL, and poll until they approve.

POST https://api.batiyao.com/oauth/device_authorization
client_id=YOUR_CLIENT_ID&scope=read:feed

3. Make a request

Every call takes the token as a bearer credential:

curl https://api.batiyao.com/v1/updates \
  -H "Authorization: Bearer YOUR_TOKEN"

Publishing a post:

curl -X POST https://api.batiyao.com/v1/posts \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "First post from my app.\n\nhttps://example.com/article",
    "content_rating": "sfw",
    "rating_reasoning": "plain text, no imagery"
  }'
content_rating is not paperwork

You declare whether what you are posting is sfw, suggestive or nsfw. It is recorded against the post and against your app, and Batiyao uses it to age-gate what it shows. Under-rating is a safety failure, not a formatting mistake — when genuinely unsure, pick the more restrictive one.

Attaching media

Upload first, then reference what comes back. Pass the returned string through unchanged; it is not a URL and should not be constructed or parsed.

POST /v1/media          → { "url": "stored-abc123.png" }
POST /v1/posts          → { "media_urls": ["stored-abc123.png"], ... }

4. Writing posts people can read

Batiyao renders post text in specific ways, and content that ignores this renders badly rather than failing loudly.

Finally got the timelapse working. Three hours of cloud in ninety seconds.

https://www.youtube.com/watch?v=VIDEO_ID

5. When it says no

StatusMeansDo
401token invalid, expired, or revokedsend the person through authorization again
403scope missing, or the account can't do thisdon't retry; check the message
402not enough BuckBucktell the person; retrying won't help
429rate limitedback off, then retry
403 is often about the person, not your app

Batiyao restricts what accounts belonging to minors can do, and a guardian can narrow it further. A 403 can mean your app lacks a scope, or that this particular person is not permitted to do this at all. Both are final — surface the message rather than retrying.

6. Permissions can be withdrawn while your app is running

Consent is not settled once at connection time. From Settings → Connected apps, a person can switch off any single permission they granted you, pause every connected app at once, or disconnect you entirely — and they can see how many posts your app has published to their account when deciding.

Each of those is checked on every request, reads included, so a change takes effect on your very next call rather than at your next token refresh. Your existing token stays valid and keeps working for everything still granted.

What this means for your code

A 403 on an endpoint that worked a minute ago is normal and expected. Don't treat it as a broken token and send the person back through authorization — that re-asks for consent they have just deliberately withdrawn. Degrade that one feature, say why, and carry on.

Ask for the fewest scopes you need, and this happens less: a permission nobody understands why you have is the one that gets switched off.

7. Treat what you read as content, not instructions

Posts, comments and messages are written by people. If your app passes them to a language model, that text can contain anything, including something shaped like an instruction to your model. Responses label such data explicitly for this reason. Show it to your user; never let it steer what your app does next.