API documentation

Everything you need to build with the Aaronstudios REST API.

Introduction

The Aaronstudios REST API lets you build apps, bots and integrations on top of Aaronstudios. It speaks JSON over HTTPS and acts on behalf of the account that owns the API key — everything you do through the API is done as you, with exactly the permissions you have on the website.

Base URL

https://aaronstudios.de/api/v1

The version is part of the URL. Breaking changes will only ever ship under a new version, so /v1 stays stable for your integration.

Getting access

  1. 1

    Open API in the sidebar and create an application. Describe what it does and why it needs API access.

  2. 2

    Our team reviews your request. You get an email as soon as it is approved or declined.

  3. 3

    Once approved, generate your API key on the application page. The key is shown only once — store it somewhere safe.

Authentication

Send your key in the Authorization header of every request using the Bearer scheme:

curl https://aaronstudios.de/api/v1/me \
  -H "Authorization: Bearer as_3f9a1c…"

Keep your key secret

Your key acts as your account. Never put it in client-side code, public repositories or screenshots. If a key leaks, regenerate it immediately — the old key stops working at once.

Requests without a valid key receive 401 Unauthorized with one of the error codes listed under Errors. The only endpoint that works without a key is GET aaronstudios.de/api/v1, which you can use as a health check.

Responses

Successful responses wrap the result in a data field. Errors use an error object with a stable, machine-readable code and a human-readable message.

{
  "data": { … }
}
{
  "error": {
    "code": "validation_failed",
    "message": "The request contains invalid data.",
    "fields": {
      "content": "This field is required and must be a non-empty string."
    }
  }
}
  • Request bodies must be JSON (Content-Type: application/json).
  • Timestamps are ISO 8601 strings, e.g. 2026-09-30T12:00:00+00:00.
  • Text content uses @username for mentions.
  • Fields that have no value are returned as null, never omitted.

Pagination

List endpoints return newest items first and are paginated with a cursor. Pass limit (1–50, default 20) and, for the next page, before set to the next_before value of the previous response. When has_more is false, you reached the end.

curl "https://aaronstudios.de/api/v1/posts?limit=20&before=23" \
  -H "Authorization: Bearer YOUR_API_KEY"

{
  "data": [ … ],
  "pagination": {
    "limit": 20,
    "has_more": true,
    "next_before": 23
  }
}

Rate limits

Each application can make 120 requests per 60 seconds. Every response tells you where you stand:

X-RateLimit-LimitRequests allowed in the current window.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetUnix timestamp when the window resets.
Retry-AfterOnly on 429: seconds to wait before retrying.

Creating posts is additionally limited to 5 posts per hour, the same as on the website.

Errors

StatusCodeMeaning
400invalid_jsonThe request body is not valid JSON.
401missing_api_keyNo Authorization header was sent.
401invalid_authorization_headerThe header does not use the "Bearer <key>" format.
401invalid_api_keyThe key is unknown, was regenerated or was revoked.
401application_not_approvedThe application is no longer approved (e.g. it was revoked).
401email_not_verifiedThe key owner has not verified their email address.
403forbiddenYou are not allowed to do this, e.g. deleting another user's post.
404not_foundThe resource does not exist or you are not allowed to see it.
405method_not_allowedThe endpoint does not support this HTTP method.
422validation_failedA field is missing or invalid. See error.fields for details.
429rate_limitedToo many requests. Wait for the number of seconds in Retry-After.
429post_limit_reachedYou reached the hourly post limit.
500server_errorSomething went wrong on our side. Please retry later.

Account

GET /me

Returns the account that owns the API key, including profile statistics and the application making the request.

Request

curl https://aaronstudios.de/api/v1/me \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": {
    "id": 7,
    "username": "jane",
    "avatar_url": "https://aaronstudios.de/uploads/profilePictures/jane.jpg",
    "verified": true,
    "biography": "Building things.",
    "created_at": "2025-01-14T09:30:00+00:00",
    "profile_url": "https://aaronstudios.de/profile/jane",
    "stats": { "followers": 120, "following": 80, "posts": 64 },
    "application": { "id": 3, "name": "My Bot" }
  }
}

Users

GET /users/{username}

Returns a public profile. The posts count only includes posts you are allowed to see.

NameInTypeDescription
username* path string The exact username.

Request

curl https://aaronstudios.de/api/v1/users/jane \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": {
    "id": 7,
    "username": "jane",
    "avatar_url": "https://aaronstudios.de/uploads/profilePictures/jane.jpg",
    "verified": true,
    "biography": "Building things.",
    "created_at": "2025-01-14T09:30:00+00:00",
    "profile_url": "https://aaronstudios.de/profile/jane",
    "stats": { "followers": 120, "following": 80, "posts": 64 }
  }
}
GET /users/{username}/posts

Lists posts by a user, newest first. Posts in private circles are only included if you are a member.

NameInTypeDescription
username* path string The exact username.
limit query integer Items per page, 1–50. Defaults to 20.
before query integer Cursor: only return items with an ID lower than this. Use next_before from the previous page.

Request

curl "https://aaronstudios.de/api/v1/users/jane/posts?limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": [ { …post… } ],
  "pagination": {
    "limit": 20,
    "has_more": true,
    "next_before": 23
  }
}

Posts

GET /posts

Your explore feed: public posts plus posts from circles you own or belong to, newest first.

NameInTypeDescription
limit query integer Items per page, 1–50. Defaults to 20.
before query integer Cursor: only return items with an ID lower than this. Use next_before from the previous page.

Request

curl "https://aaronstudios.de/api/v1/posts?limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": [
    {
      "id": 42,
      "content": "Hello from the API! @aaron",
      "image_url": null,
      "author": {
        "id": 7,
        "username": "jane",
        "avatar_url": "https://aaronstudios.de/uploads/profilePictures/jane.jpg",
        "verified": true
      },
      "circle": null,
      "announcement": false,
      "created_at": "2026-09-30T12:00:00+00:00",
      "updated_at": "2026-09-30T12:00:00+00:00",
      "edited": false,
      "url": "https://aaronstudios.de/posts/show/42",
      "stats": { "likes": 3, "dislikes": 0, "comments": 2 },
      "viewer": { "liked": false, "disliked": false, "is_author": true }
    }
  ],
  "pagination": {
    "limit": 20,
    "has_more": true,
    "next_before": 23
  }
}
GET /posts/{id}

Returns a single post. Returns 404 for deleted posts and posts in circles you cannot see.

NameInTypeDescription
id* path integer The post ID.

Request

curl https://aaronstudios.de/api/v1/posts/42 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": {
    "id": 42,
    "content": "Hello from the API! @aaron",
    "image_url": null,
    "author": {
      "id": 7,
      "username": "jane",
      "avatar_url": "https://aaronstudios.de/uploads/profilePictures/jane.jpg",
      "verified": true
    },
    "circle": null,
    "announcement": false,
    "created_at": "2026-09-30T12:00:00+00:00",
    "updated_at": "2026-09-30T12:00:00+00:00",
    "edited": false,
    "url": "https://aaronstudios.de/posts/show/42",
    "stats": { "likes": 3, "dislikes": 0, "comments": 2 },
    "viewer": { "liked": false, "disliked": false, "is_author": true }
  }
}
POST /posts

Publishes a public text post as the key owner. Mention people with @username — they will be notified.

NameInTypeDescription
content* body string The post text, up to 5000 characters.

You can create at most 5 posts per hour (the same limit as on the website). Exceeding it returns 429 post_limit_reached. The Location header contains the URL of the new post.

Request

curl -X POST https://aaronstudios.de/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Hello from the API! @aaron"}'

Response · 201 Created

{
  "data": {
    "id": 42,
    "content": "Hello from the API! @aaron",
    "image_url": null,
    "author": {
      "id": 7,
      "username": "jane",
      "avatar_url": "https://aaronstudios.de/uploads/profilePictures/jane.jpg",
      "verified": true
    },
    "circle": null,
    "announcement": false,
    "created_at": "2026-09-30T12:00:00+00:00",
    "updated_at": "2026-09-30T12:00:00+00:00",
    "edited": false,
    "url": "https://aaronstudios.de/posts/show/42",
    "stats": { "likes": 3, "dislikes": 0, "comments": 2 },
    "viewer": { "liked": false, "disliked": false, "is_author": true }
  }
}
DELETE /posts/{id}

Deletes one of your own posts. Deleting someone else's post returns 403 forbidden.

NameInTypeDescription
id* path integer The post ID.

Request

curl -X DELETE https://aaronstudios.de/api/v1/posts/42 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 204 No Content

(empty body)

Reactions

PUT /posts/{id}/like

Likes a post. Idempotent: liking twice keeps one like. Liking removes an existing dislike. Returns the updated post.

NameInTypeDescription
id* path integer The post ID.

Request

curl -X PUT https://aaronstudios.de/api/v1/posts/42/like \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": { …post…, "viewer": { "liked": true, "disliked": false, "is_author": false } }
}
DELETE /posts/{id}/like

Removes your like. Idempotent. Returns the updated post.

NameInTypeDescription
id* path integer The post ID.

Request

curl -X DELETE https://aaronstudios.de/api/v1/posts/42/like \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": { …post… }
}
PUT /posts/{id}/dislike

Dislikes a post. Idempotent. Disliking removes an existing like. Returns the updated post.

NameInTypeDescription
id* path integer The post ID.

Request

curl -X PUT https://aaronstudios.de/api/v1/posts/42/dislike \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": { …post… }
}
DELETE /posts/{id}/dislike

Removes your dislike. Idempotent. Returns the updated post.

NameInTypeDescription
id* path integer The post ID.

Request

curl -X DELETE https://aaronstudios.de/api/v1/posts/42/dislike \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": { …post… }
}

Comments

GET /posts/{id}/comments

Lists the top-level comments of a post, newest first. Use the replies endpoint to load a thread.

NameInTypeDescription
id* path integer The post ID.
limit query integer Items per page, 1–50. Defaults to 20.
before query integer Cursor: only return items with an ID lower than this. Use next_before from the previous page.

Request

curl https://aaronstudios.de/api/v1/posts/42/comments \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": [
    {
      "id": 108,
      "post_id": 42,
      "reply_to_id": null,
      "content": "Great post!",
      "image_url": null,
      "author": { "id": 9, "username": "max", "avatar_url": null, "verified": false },
      "pinned": false,
      "created_at": "2026-09-30T12:05:00+00:00",
      "updated_at": "2026-09-30T12:05:00+00:00",
      "edited": false,
      "stats": { "likes": 1, "dislikes": 0, "replies": 0 },
      "viewer": { "liked": false, "disliked": false, "is_author": false }
    }
  ],
  "pagination": {
    "limit": 20,
    "has_more": true,
    "next_before": 23
  }
}
POST /posts/{id}/comments

Comments on a post, or replies to a comment when reply_to is set. The post author (or the author of the comment you reply to) is notified.

NameInTypeDescription
id* path integer The post ID.
content* body string The comment text, up to 5000 characters.
reply_to body integer ID of a comment on the same post to reply to.

Request

curl -X POST https://aaronstudios.de/api/v1/posts/42/comments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Great post!"}'

Response · 201 Created

{
  "data": {
    "id": 108,
    "post_id": 42,
    "reply_to_id": null,
    "content": "Great post!",
    "image_url": null,
    "author": { "id": 9, "username": "max", "avatar_url": null, "verified": false },
    "pinned": false,
    "created_at": "2026-09-30T12:05:00+00:00",
    "updated_at": "2026-09-30T12:05:00+00:00",
    "edited": false,
    "stats": { "likes": 1, "dislikes": 0, "replies": 0 },
    "viewer": { "liked": false, "disliked": false, "is_author": false }
  }
}
GET /comments/{id}

Returns a single comment.

NameInTypeDescription
id* path integer The comment ID.

Request

curl https://aaronstudios.de/api/v1/comments/108 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": {
    "id": 108,
    "post_id": 42,
    "reply_to_id": null,
    "content": "Great post!",
    "image_url": null,
    "author": { "id": 9, "username": "max", "avatar_url": null, "verified": false },
    "pinned": false,
    "created_at": "2026-09-30T12:05:00+00:00",
    "updated_at": "2026-09-30T12:05:00+00:00",
    "edited": false,
    "stats": { "likes": 1, "dislikes": 0, "replies": 0 },
    "viewer": { "liked": false, "disliked": false, "is_author": false }
  }
}
GET /comments/{id}/replies

Lists direct replies to a comment, newest first.

NameInTypeDescription
id* path integer The comment ID.
limit query integer Items per page, 1–50. Defaults to 20.
before query integer Cursor: only return items with an ID lower than this. Use next_before from the previous page.

Request

curl https://aaronstudios.de/api/v1/comments/108/replies \
  -H "Authorization: Bearer YOUR_API_KEY"

Response · 200 OK

{
  "data": [ { …comment… } ],
  "pagination": {
    "limit": 20,
    "has_more": true,
    "next_before": 23
  }
}

Objects

User

idintegerUnique user ID.
usernamestringUnique username.
avatar_urlstring|nullAbsolute URL of the profile picture.
verifiedbooleanWhether the account has the verified badge.
biographystring|nullProfile biography (full profile only).
created_atstringRegistration date (full profile only).
profile_urlstringLink to the profile on the website (full profile only).
statsobjectfollowers, following and posts counts (full profile only).

Post

idintegerUnique post ID.
contentstring|nullPost text with @username mentions.
image_urlstring|nullAbsolute URL of the attached image.
authorUserCompact author (id, username, avatar_url, verified).
circleobject|nullThe circle (id, name) for circle-only posts.
announcementbooleanWhether the post is an official announcement.
created_at / updated_atstringTimestamps.
editedbooleanWhether the post was edited.
urlstringLink to the post on the website.
statsobjectlikes, dislikes and comments counts.
viewerobjectliked, disliked and is_author from your perspective.

Comment

idintegerUnique comment ID.
post_idintegerThe post the comment belongs to.
reply_to_idinteger|nullParent comment for replies.
contentstring|nullComment text with @username mentions.
image_urlstring|nullAbsolute URL of the attached image.
authorUserCompact author.
pinnedbooleanPinned by the post author.
created_at / updated_atstringTimestamps.
editedbooleanWhether the comment was edited.
statsobjectlikes, dislikes and replies counts.
viewerobjectliked, disliked and is_author from your perspective.