reverse-engineering status.cafe's api
finding a way to programatically change statuses on status.cafe
by nova - 12/23/25, 05:00 PM
894 words ( 5 min to read)
Status.Cafe is a website that provides a feed of statuses from people all over the Internet. It's great for anyone who wants a chill way to hear about peoples' days without the annoyance of social media apps. However, what if you want to programatically update your status? They don't have OAuth, and they don't document the API. I am the creator of an app for distributing your status across platforms called Universal Status. Thus, I wanted to build a way to push updates to Status.Cafe with code.
Original Attempts
As always when I want to figure something like this out, I opened my browser's DevTools and changed my status. It made a POST request to https://status.cafe/add. This had three parameters: "face", "content", and "gorilla.csrf.Token".
content was the status I had entered, and face was the emoji I selected. But what about gorilla.csrf.Token?
I tried omitting it from the request and resending it, and that failed. I did a bit of poking around in the HTML, and saw that was coming from a hidden field in the <form>.
I also noticed that there were two cookies, _gorilla_csrf and status. The CSRF one stayed the same, but the status one changed every time the status was updated.
In my research to work out the exact logic, I found the Status.Cafe source code, which was interesting.
Recreating the Status Form
It wasn't very complicated for me to make this in TypeScript. I just had to get that hidden form field with the CSRF token, and then insert the user's emoji and status text choices. I needed a way to read HTML in a fetch response, so I used the jsdom library. After a lot of work, I made this for the token extraction:
const csrfRes = await fetch(https://status.cafe);
const csrfToken = new JSDOM(await csrfRes.text()).window.document.getElementsByName("gorilla.csrf.Token")[0]).getAttribute("value")!;
The website didn't give me the CSRF token unless I made the fetch with the appropriate cookies, but I figured out how to do that later. Then, I just had to turn this into a full request chain:
const csrfRes = await fetch(https://status.cafe);
const formData = new FormData();
formData.append("gorilla.csrf.Token", (new JSDOM(await csrfRes.text()).window.document.getElementsByName("gorilla.csrf.Token")[0]).getAttribute("value")!);
formData.append("face", emoji);
formData.append("content", status);
const statusRes = await fetch("https://status.cafe/add", {
method: "POST",
body: formData
});
This worked if I passed my own cookies into both requests, but that wasn't practical for production. I had to figure out authentication.
Authenticating Users
Logging in was relatively simple. Make a call to https://status.cafe/check-login with name and password attributes (in search parameter form), and it sets a "status" cookie if the credentials are correct.
Just like the other form, this one had a hidden CSRF token in its user-facing form (https://status.cafe/login). We can use the same code as before to get that token, just changing the csrfRes fetch to the login page instead of the main page.
To get the initial CSRF cookie, I made an unauthenticated call to the home page, then used that cookie for the future calls.
// something here to fetch the initial cookie
const csrfRes = await fetch("https://status.cafe/login");
const csrfToken = (new JSDOM(await csrfRes.text()).window.document.getElementsByName("gorilla.csrf.Token")[0])!.getAttribute("value")!;
const loginRes = await jarFetch("https://status.cafe/check-login", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
name: body.username,
password: body.password,
"gorilla.csrf.Token": csrfToken
}).toString(),
redirect: "follow"
});
The Cookie Jar Problem
Storing cookies and passing them between fetch calls was very problematic for me, and a real pain point in the code.
I fixed this by using the fetch-cookie package, and it solved my problem.
I just needed to insert cookies to the jar with jarFetch.cookieJar.setCookie(), and pull the final ones with some other code later on.
This turned my earlier status-update request into this:
const jarFetch = fetchCookie(fetch);
try {
jarFetch.cookieJar.setCookie(statusCafeCookie, "https://status.cafe", { ignoreError: false });
jarFetch.cookieJar.setCookie(statusCafeCSRF, "https://status.cafe", { ignoreError: false });
const csrfRes = await jarFetch("https://status.cafe");
const formData = new FormData();
formData.append("gorilla.csrf.Token", (new JSDOM(await csrfRes.text()).window.document.getElementsByName("gorilla.csrf.Token")[0]).getAttribute("value")!);
formData.append("face", emoji);
formData.append("content", status);
await jarFetch("https://status.cafe/add", {
method: "POST",
body: formData
});
const cookies = (await jarFetch.cookieJar.getCookieString("https://status.cafe")).split("; ");
const userCookie = cookies.find(c => c.startsWith("status="));
const csrfCookie = cookies.find(c => c.startsWith("_gorilla_csrf="));
Similarly, my login one became:
const jarFetch = fetchCookie(fetch);
await jarFetch("https://status.cafe");
const csrfRes = await jarFetch("https://status.cafe/login");
const csrfToken = (new JSDOM(await csrfRes.text()).window.document.getElementsByName("gorilla.csrf.Token")[0])!.getAttribute("value")!;
await jarFetch("https://status.cafe/check-login", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
name: body.username,
password: body.password,
"gorilla.csrf.Token": csrfToken
}).toString(),
redirect: "follow"
});
Saving Cookies
Since this cookie jar wasn't stored, I needed a way to save these cookies to a database. Universal Status uses MongoDB, so this was simple enough.
const cookies = (await jarFetch.cookieJar.getCookieString("https://status.cafe")).split("; ");
const userCookie = cookies.find(c => c.startsWith("status="));
const csrfCookie = cookies.find(c => c.startsWith("_gorilla_csrf="));
if (!userCookie) return false;
await client.db(process.env.MONGODB_DB).collection<UserDoc>("statuses").updateOne({ user: session.user.email }, {
$set: { statusCafeCookie: userCookie, statusCafeCSRF: csrfCookie }
});
return true;
I added this both after status updates and after login, and it worked!
Testing
I tried logging in, and after some troubleshooting to get the above cookie-saving code correct, I had it working. I changed my status in Universal Status, and it pushed the change to Status.Cafe! Then, I tried with the 🌌 emoji, which isn't on Status.Cafe's list. It worked! I successfully was able to add statuses with nearly any emoji. For some reason, pride flags don't seem to work.
Final Implementations
The final implementations of this code can be found in the Universal Status GitHub, specifically this API Route and this part of platforms.ts. It's been functioning for a few months now, and I haven't experienced any issues.