tellmelotellmelo.
ImprintPrivacyTerms of useReport copyrightContactAPIApps

API

This is a translation. The German version is authoritative. Where this text differs from it, the German text applies. You can switch language at the bottom of the page.

Getting started

  • Overview
  • Quick start
  • Your key
  • What a key may do
  • What no key can do

Basics

  • Requests and answers
  • Pages
  • Only what is new
  • Limits
  • Headers
  • When something does not work
  • Versions

Endpoints

  • Endpoints
  • The way in
    • GET /
  • Posts
    • GET /posts
    • GET /posts/{id}
    • POST /posts
    • DELETE /posts/{id}
  • Replies
    • GET /posts/{id}/replies
    • POST /posts/{id}/replies
  • Polls
    • POST /posts/{id}/vote
  • Profiles
    • GET /profiles/{handle}
    • GET /profiles/{handle}/posts
  • Communities
    • GET /communities
    • GET /communities/{id}
    • GET /communities/{id}/posts
  • Discover
    • GET /tags
    • GET /search
    • GET /emojis
  • Your account
    • GET /me
    • GET /feed
  • Notifications
    • GET /notifications
    • POST /notifications/read
  • Messages
    • GET /conversations
    • GET /conversations/{with}/messages
    • POST /conversations/{with}/messages
    • POST /conversations/{with}/read
  • Relations
    • POST /relations

Objects

  • Objects
  • Service
  • RateLimit
  • Post
  • Reply
  • Counts
  • Media
  • Poll
  • PollOption
  • ProfileBrief
  • Profile
  • CommunityBrief
  • Community
  • Tag
  • SearchResult
  • Emoji
  • Notification
  • Actor
  • Conversation
  • Message
  • Created
  • Ok

More

  • The machine-readable description
  • What became of v1
  • What we expect
Contents

Getting started

  • Overview
  • Quick start
  • Your key
  • What a key may do
  • What no key can do

Basics

  • Requests and answers
  • Pages
  • Only what is new
  • Limits
  • Headers
  • When something does not work
  • Versions

Endpoints

  • Endpoints
  • The way in
    • GET /
  • Posts
    • GET /posts
    • GET /posts/{id}
    • POST /posts
    • DELETE /posts/{id}
  • Replies
    • GET /posts/{id}/replies
    • POST /posts/{id}/replies
  • Polls
    • POST /posts/{id}/vote
  • Profiles
    • GET /profiles/{handle}
    • GET /profiles/{handle}/posts
  • Communities
    • GET /communities
    • GET /communities/{id}
    • GET /communities/{id}/posts
  • Discover
    • GET /tags
    • GET /search
    • GET /emojis
  • Your account
    • GET /me
    • GET /feed
  • Notifications
    • GET /notifications
    • POST /notifications/read
  • Messages
    • GET /conversations
    • GET /conversations/{with}/messages
    • POST /conversations/{with}/messages
    • POST /conversations/{with}/read
  • Relations
    • POST /relations

Objects

  • Objects
  • Service
  • RateLimit
  • Post
  • Reply
  • Counts
  • Media
  • Poll
  • PollOption
  • ProfileBrief
  • Profile
  • CommunityBrief
  • Community
  • Tag
  • SearchResult
  • Emoji
  • Notification
  • Actor
  • Conversation
  • Message
  • Created
  • Ok

More

  • The machine-readable description
  • What became of v1
  • What we expect

Overview

v2 is the API of tellmelo for your own programs. Every request carries a key, every answer is JSON, and every list pages the same way. What happens behind a key runs through the same rules as a click in the application: rate limits, suspensions, withdrawn rights and the settings of this instance apply here just the same.

  • Every path begins with /api/v2 at this instance’s own address, the one used in the examples on this page.
  • Requests and answers are JSON in UTF-8. Field names, codes and values are English and stay English.
  • A key acts as your account and never beyond it: what you cannot see in the application, no key can read either.
https://tellmelo.com/api/v2

Quick start

  1. Create a key under “Settings → App & data → API” and copy it. It is shown only once.
  2. Ask the root with it. The answer says which version is running, what the key may do and how many requests it has left this minute.
  3. From there on every path works the same way: the key in the header, JSON back, and for lists items and next.
curl -H "Authorization: Bearer tm_key_…" https://tellmelo.com/api/v2
{
  "name": "tellmelo",
  "version": "2.6",
  "scope": "read write",
  "rateLimit": {
    "limit": 120,
    "remaining": 117
  },
  "docs": "/legal/api",
  "spec": "/api/v2/openapi.json"
}

Where to go next: /me for your own profile, /posts for what is public, /feed for what the application shows you.

Your key

There is exactly one key per account, bound to your user id. It begins with tm_key_, so that a scanner recognises it where it does not belong. It is shown exactly once, at the moment it is created; afterwards only its fingerprint is held here, and nobody can read it back to you, neither we nor you. Whoever mislays it creates a new one; the old one stops being valid at that moment.

You create it under “Settings → App & data → API”. It travels in the header of the request, as Authorization: Bearer tm_key_… or as X-Tellmelo-Key. Not in the address bar, because what stands there ends up in access logs, in the browser history and in the referer of every link.

curl -H "Authorization: Bearer tm_key_…" \
  https://tellmelo.com/api/v2/posts

Or, if that is more convenient, in a header of its own:

curl -H "X-Tellmelo-Key: tm_key_…" https://tellmelo.com/api/v2

Not in the address bar. What stands there ends up in the access log of every server in between, in the browser history and in the referer of every link. A key does not belong there.

The same key also loads images. Every url in an answer points to /api/media on this instance; send the key in the header there too, and the picture comes back, as far as your account may see it.

curl -H "Authorization: Bearer tm_key_…" \
  -o picture.webp "https://tellmelo.com/api/media?id=…"

What a key may do

A key carries two rights, and every path in the tables below says which of them it needs. Two and not five: a right you cannot explain to yourself in one sentence is a right you tick without reading. And the honest line runs between looking and changing.

  • read: looking. The public posts, profiles, communities and tags, and your own account as you see it: feed, notifications, conversations, messages.
  • write: changing. Everything that happens under your name: posting, replying, voting, sending messages, following, joining, blocking, marking notifications as read.

What no key can do

No key reaches your account data: no password, no email address, no account kind, no place, no role, no deletion. Nor sessions, nor push devices, nor the keys themselves: one that could issue keys could no longer be switched off. Nor administration, nor community leadership, because dissolving a community or handing it on is a decision about other people’s posts. What the password guards, no key may do: a secret lying in a script on somebody else’s machine must not be able to do what the login can. Taking over an account still costs the login.

Requests and answers

  • JSON everywhere. Every answer is application/json in UTF-8, errors included. Only images come as images.
  • What you send. A POST carries its fields as a JSON object in the body. Query parameters fill in what the body does not name; where both name the same field, the body wins.
  • Times are whole numbers of milliseconds since 1 January 1970, UTC, the way the database holds them. There is no time zone to get wrong.
  • Ids are strings. Do not take them apart and do not rely on their form; compare them only as a whole.
  • Every field is always there. What does not exist is null, never missing; a count is always a number, a list always a list, empty if need be.
  • Texts are plain text, exactly as written. #tags, @names and links stay as they are, and so do the instance’s own emojis, as :name:; their pictures are listed under /emojis.
  • Images are addresses relative to this instance, /api/media?id=…. Load them with the same key in the header; without one the answer is 401.

Pages

Every list takes limit and cursor and answers with items and next. You continue by sending the next of the last answer back as cursor; when next is empty, the end is reached. A full page can still be the last one, and whoever stops only at an empty page asks once too often. The cursor is opaque: a place in a list, not a point in time. Do not take it apart and do not build one yourself; what is inside it may change without any path changing.

{ "items": [ … ], "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx" }

curl -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/posts?limit=20&cursor=MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"

A page holds 20 entries without limit, 50 at most. A few lists are not paged at all: they say so at their path, and their next is always null.

Only what is new

Every answer carries an ETag. A program that asks again and again should send the last one back as If-None-Match: if nothing has changed since, the answer is 304 and carries no body. That is the difference between a list that crosses the wire every minute and one that crosses it when there is something in it, for your machine as much as for this one.

ETag: W/"3Qk1mJ7fQe2Yb0sVxT9aL4pNdRc"
If-None-Match: W/"3Qk1mJ7fQe2Yb0sVxT9aL4pNdRc"  →  304

Two exceptions, both on purpose: /feed carries no ETag, because its order moves with time and every answer is different; and a write never answers 304. Two identical posts are two posts.

Limits

A key may make 120 requests per minute, 600 with the “Organization” plan, unless the administration has set a different number for it. The limit counts per key, not per account and not per address. Above it the answer is 429 and nothing has happened: the request was refused, not carried out. Wait a minute and send it again; a program that runs into it regularly should slow down rather than ask again at once.

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1758624060

Besides the key’s own limit, writes count against the same limits as in the application: how many posts, replies or relations in a few minutes. Those also answer 429 with the code rate_limited.

Headers

The names are the ones every client library already knows. None is required except the key.

What you send

HeaderMeaning
AuthorizationCarries the key: Bearer tm_key_…. The usual way.
X-Tellmelo-KeyThe key, as an alternative to Authorization, for tools that use that header for something else.
Content-Typeapplication/json, for a request with a body.
If-None-MatchThe ETag of the last answer. If nothing has changed since, the answer is 304 without a body.

What comes back

HeaderMeaning
ETagThe fingerprint of this answer, marked as weak (W/). Send it back as If-None-Match.
X-RateLimit-LimitHow many requests this key may make per minute.
X-RateLimit-RemainingHow many of them are left in the current minute.
X-RateLimit-ResetWhen the next minute begins, in seconds since 1970, UTC.
X-Tellmelo-ScopeWhat this key may do: read or read write.
Retry-AfterWith a 429: how many seconds to wait before asking again.
WWW-AuthenticateWith a 401 for a missing key: Bearer, the way a key is expected.
Cache-Controlno-store: no proxy in between may keep an answer, because it depends on the key that asked. Your own program may still check it with the ETag.

When something does not work

Errors come as JSON with two fields: a fixed English code in error that your program can compare against, and a sentence in message for the person in front of it, plus the status code that belongs to it. The code stays; the sentence may change, and it may change in any language: a program that compares the sentence breaks on a day nobody announced.

{ "error": "rate_limited",
  "message": "Too many requests. Try again in a minute.",
  "limit": 120 }

Some errors bring one more field: docs with a 401 for a missing key, limit with a 429.

StatusCodeMeaning
400bad_requestThe request cannot be read, a required field is missing, or a value is not one of those allowed. The message says which.
401key_missingNo key in the header.
401key_invalidThe key is not valid: mistyped, replaced by a new one, or its account is suspended. Which of these is deliberately not said.
401unauthorizedRefused as not signed in, for a reason other than the key. Rare, and a reason to look at the account behind the key.
403scope_missingThe key lacks the right this path needs: usually write on a key that may only read.
403account_data_lockedThis touches your account data, which no key can reach: password, email address, deletion and the like.
403forbiddenYour account may not do this: the same refusal as in the application, for example a withdrawn right or a block.
404unknown_pathThere is no such path, or not with this method.
404not_foundThe path exists, but what it names does not, or you may not see it. The two are not told apart.
409conflictIt collides with what is already there, for example a name that is taken.
413too_largeToo large: a text or a request beyond what this instance accepts.
422unprocessableReadable, but not possible in this form.
429rate_limitedToo many requests. Nothing was done; wait and send it again.
500internal_errorSomething went wrong on our side, not on yours. It is logged in full on the server.

Versions

The version is in the path. As long as it says v2, these paths and their fields stay as they are; what is added comes alongside.

Additions raise the second number: 2.1 added /emojis and marking a conversation as read. 2.4 removed the contentWarning field again, because content warnings no longer exist. 2.5 removed the alt field on images, because image descriptions no longer exist. 2.6 adds the notification kind team for new work for the team. Every path says since which version it exists, and the root says which version is running.

Endpoints

All paths at a glance, then each one in detail: what it takes, an example request and what comes back.

PathRightWhat for
The way in
GET/api/v2readThe way in: which version this API speaks and what this instance is called.
Posts
GET/api/v2/postsreadThe public posts, newest first.
GET/api/v2/posts/{id}readA single post, by its id.
POST/api/v2/postswritePublish a post.
DELETE/api/v2/posts/{id}writeRemove one of your own posts.
Replies
GET/api/v2/posts/{id}/repliesreadThe replies to a post, as you see them: what a blocked account wrote stays out.
POST/api/v2/posts/{id}/replieswriteReply to a post.
Polls
POST/api/v2/posts/{id}/votewriteTake part in a poll.
Profiles
GET/api/v2/profiles/{handle}readA profile, by its short name.
GET/api/v2/profiles/{handle}/postsreadThe public posts of one profile, newest first.
Communities
GET/api/v2/communitiesreadThe open communities of this instance.
GET/api/v2/communities/{id}readA single community, by its id.
GET/api/v2/communities/{id}/postsreadThe public posts from one community, newest first.
Discover
GET/api/v2/tagsreadThe tags that are currently running.
GET/api/v2/searchreadA search over posts, names, short names and tags.
GET/api/v2/emojisreadThe instance’s own emojis, with the address of their pictures.
Your account
GET/api/v2/mereadYour own profile, with the id the other paths expect.
GET/api/v2/feedreadYour own feed, the way the application puts it together.
Notifications
GET/api/v2/notificationsreadYour own notifications, newest first, pageable, so a program can catch up on what it missed.
POST/api/v2/notifications/readwriteMark your notifications as read.
Messages
GET/api/v2/conversationsreadYour inbox: one row per conversation, the most recent first.
GET/api/v2/conversations/{with}/messagesreadThe messages of one conversation, newest first.
POST/api/v2/conversations/{with}/messageswriteSend a message in a conversation.
POST/api/v2/conversations/{with}/readwriteMark every message of a conversation as read.
Relations
POST/api/v2/relationswriteLike, save, repost, follow, join, block, mute, depending on kind.

The way in

The first request of every program: does the key work, and what may it do?

GET/api/v2

The way in: which version this API speaks and what this instance is called.

  • Right read
  • with ETag
  • since v2.0

Without a slash at the end: /api/v2/ redirects to /api/v2 with 308, and not every program follows.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2"

Answer

Answers with one Service.

Example
{
  "name": "tellmelo",
  "version": "2.6",
  "scope": "read write",
  "rateLimit": {
    "limit": 120,
    "remaining": 117
  },
  "docs": "/legal/api",
  "spec": "/api/v2/openapi.json"
}

Possible statuses: 200304401429

Posts

Reading public posts, publishing your own and removing them again.

GET/api/v2/posts

The public posts, newest first.

  • Right read
  • pageable
  • with ETag
  • since v2.0

On the public paths reactions are not counted: counts.likes and counts.reposts are 0 and pinned is false (a cut, not a measurement).

Posts inside a community are not reached here, neither in the list nor by their id. They are read under /communities/{id}/posts.

Parameters

NameTypeMeaning
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/posts?limit=20"

Answer

Answers with a page of Post, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "text": "The bees are back in the garden 🐝 #garden",
      "kind": "poll",
      "author": {
        "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
        "handle": "mara",
        "name": "Mara 🌻",
        "verified": true,
        "accountKind": "person"
      },
      "community": {
        "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
        "name": "Urban Gardening"
      },
      "media": [
        {
          "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
        }
      ],
      "poll": {
        "options": [
          {
            "text": "Lavender",
            "votes": 12
          },
          {
            "text": "Sunflowers",
            "votes": 7
          },
          {
            "text": "Clover",
            "votes": 3
          }
        ],
        "total": 22,
        "multiple": false,
        "endsAt": 1758710400000,
        "running": true,
        "resultsVisible": true,
        "myVotes": [
          0
        ]
      },
      "quotes": null,
      "continues": null,
      "counts": {
        "replies": 4,
        "likes": 31,
        "reposts": 2
      },
      "pinned": false,
      "createdAt": 1758624000000
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200304401429

GET/api/v2/posts/{id}

A single post, by its id.

  • Right read
  • with ETag
  • since v2.0

On the public paths reactions are not counted: counts.likes and counts.reposts are 0 and pinned is false (a cut, not a measurement).

Posts inside a community are not reached here, neither in the list nor by their id. They are read under /communities/{id}/posts.

Parameters

NameTypeMeaning
idin the pathstringrequiredThe id of a post.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/posts/5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1"

Answer

Answers with one Post.

Example
{
  "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
  "text": "The bees are back in the garden 🐝 #garden",
  "kind": "poll",
  "author": {
    "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
    "handle": "mara",
    "name": "Mara 🌻",
    "verified": true,
    "accountKind": "person"
  },
  "community": {
    "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
    "name": "Urban Gardening"
  },
  "media": [
    {
      "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
    }
  ],
  "poll": {
    "options": [
      {
        "text": "Lavender",
        "votes": 12
      },
      {
        "text": "Sunflowers",
        "votes": 7
      },
      {
        "text": "Clover",
        "votes": 3
      }
    ],
    "total": 22,
    "multiple": false,
    "endsAt": 1758710400000,
    "running": true,
    "resultsVisible": true,
    "myVotes": [
      0
    ]
  },
  "quotes": null,
  "continues": null,
  "counts": {
    "replies": 4,
    "likes": 31,
    "reposts": 2
  },
  "pinned": false,
  "createdAt": 1758624000000
}

Possible statuses: 200304400401404429

POST/api/v2/posts

Publish a post.

  • Right write
  • since v2.0

A post needs text or a poll. Its length, the number of poll answers and how many posts in how much time are set by this instance; beyond them the answer is 400 or 429, with a message that says which.

Images cannot be attached through the API yet, only in the application.

Parameters

NameTypeMeaning
textin the bodystringoptionalThe text itself: of the post, the reply or the message.
communityin the bodystringoptionalThe id of the community the post goes into. Without it, outside every community.
quotesin the bodystringoptionalThe id of the post this one quotes.
pollin the bodystring[]optionalThe answer options of a poll, as a list of texts. How many are allowed is set by the instance.

Example request

curl -X POST \
  -H "Authorization: Bearer tm_key_…" \
  -H "Content-Type: application/json" \
  -d '{"text":"The bees are back in the garden 🐝 #garden"}' \
  "https://tellmelo.com/api/v2/posts"

Answer

Answers with one Created.

Example
{
  "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
  "scheduledFor": null,
  "deleteAt": null
}

Possible statuses: 200400401403429

DELETE/api/v2/posts/{id}

Remove one of your own posts.

  • Right write
  • since v2.0

Parameters

NameTypeMeaning
idin the pathstringrequiredThe id of a post.

Example request

curl -X DELETE \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/posts/5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1"

Answer

Answers with one Ok.

Example
{
  "ok": true
}

Possible statuses: 200400401403404429

Replies

The conversation under a post: read as a list that parentId turns into a tree, written by replying to the post or to a reply.

GET/api/v2/posts/{id}/replies

The replies to a post, as you see them: what a blocked account wrote stays out.

  • Right read
  • pageable
  • with ETag
  • since v2.0

The list is flat and in the order of writing; parentId makes it a tree. A page holds whole threads, so a reply never arrives without the one it answers. What an account you blocked wrote, or one that blocked you, is left out. Two keys can see different lists.

Parameters

NameTypeMeaning
idin the pathstringrequiredThe id of a post.
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/posts/5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1/replies?limit=20"

Answer

Answers with a page of Reply, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "9e4b1c7d-2a3f-4d5e-8b6c-0f1e2d3c4b5a",
      "text": "Same here, the lavender is full of them.",
      "kind": "post",
      "author": {
        "id": "d4c3b2a1-6e5f-4a7b-9c8d-1e2f3a4b5c6d",
        "handle": "jon",
        "name": "Jon",
        "verified": false,
        "accountKind": "person"
      },
      "community": null,
      "media": [],
      "poll": null,
      "quotes": null,
      "continues": null,
      "counts": {
        "replies": 0,
        "likes": 3,
        "reposts": 0
      },
      "pinned": false,
      "createdAt": 1758624600000,
      "postId": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "parentId": null
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200304400401404429

POST/api/v2/posts/{id}/replies

Reply to a post.

  • Right write
  • since v2.0

Under a post in slow mode each person can reply once every 10 minutes; another reply gets 429 with the wait in the message. Slow mode does not apply to the author or to the accounts the author follows.

Parameters

NameTypeMeaning
idin the pathstringrequiredThe id of a post.
textin the bodystringrequiredThe text itself: of the post, the reply or the message.
parentIdin the bodystringoptionalThe id of the reply this one answers. Without it, straight to the post.

Example request

curl -X POST \
  -H "Authorization: Bearer tm_key_…" \
  -H "Content-Type: application/json" \
  -d '{"text":"Same here, the lavender is full of them."}' \
  "https://tellmelo.com/api/v2/posts/5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1/replies"

Answer

Answers with one Created.

Example
{
  "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
  "scheduledFor": null,
  "deleteAt": null
}

Possible statuses: 200400401403404429

Polls

A poll is a post whose poll is set. Voting has a path of its own.

POST/api/v2/posts/{id}/vote

Take part in a poll.

  • Right write
  • since v2.0

The votes per answer are null as long as the results are held back: before your own vote, while the poll is running. total is always there.

Parameters

NameTypeMeaning
idin the pathstringrequiredThe id of a post.
optionin the bodyintegerrequiredWhich answer, counted from 0. One per call: a poll with several answers is voted by calling more than once.
retractin the bodybooleanoptionalWhether this vote is being taken back: true undoes it.

Example request

curl -X POST \
  -H "Authorization: Bearer tm_key_…" \
  -H "Content-Type: application/json" \
  -d '{"option":0}' \
  "https://tellmelo.com/api/v2/posts/5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1/vote"

Answer

Answers with one Ok.

Example
{
  "ok": true
}

Possible statuses: 200400401403404429

Profiles

Public profiles by their short name, and what they have posted.

GET/api/v2/profiles/{handle}

A profile, by its short name.

  • Right read
  • with ETag
  • since v2.0

Parameters

NameTypeMeaning
handlein the pathstringrequiredThe short name of a profile.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/profiles/mara"

Answer

Answers with one Profile.

Example
{
  "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
  "handle": "mara",
  "name": "Mara 🌻",
  "verified": true,
  "accountKind": "person",
  "about": "Beekeeper, allotment, too many seeds. :tellmelo:",
  "website": "https://mara-imkerei.example/",
  "websiteVerified": true,
  "followers": 148,
  "createdAt": 1750848000000
}

Possible statuses: 200304400401404429

GET/api/v2/profiles/{handle}/posts

The public posts of one profile, newest first.

  • Right read
  • pageable
  • with ETag
  • since v2.0

On the public paths reactions are not counted: counts.likes and counts.reposts are 0 and pinned is false (a cut, not a measurement).

Posts inside a community are not reached here, neither in the list nor by their id. They are read under /communities/{id}/posts.

Parameters

NameTypeMeaning
handlein the pathstringrequiredThe short name of a profile.
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/profiles/mara/posts?limit=20"

Answer

Answers with a page of Post, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "text": "The bees are back in the garden 🐝 #garden",
      "kind": "poll",
      "author": {
        "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
        "handle": "mara",
        "name": "Mara 🌻",
        "verified": true,
        "accountKind": "person"
      },
      "community": {
        "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
        "name": "Urban Gardening"
      },
      "media": [
        {
          "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
        }
      ],
      "poll": {
        "options": [
          {
            "text": "Lavender",
            "votes": 12
          },
          {
            "text": "Sunflowers",
            "votes": 7
          },
          {
            "text": "Clover",
            "votes": 3
          }
        ],
        "total": 22,
        "multiple": false,
        "endsAt": 1758710400000,
        "running": true,
        "resultsVisible": true,
        "myVotes": [
          0
        ]
      },
      "quotes": null,
      "continues": null,
      "counts": {
        "replies": 4,
        "likes": 31,
        "reposts": 2
      },
      "pinned": false,
      "createdAt": 1758624000000
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200304400401404429

Communities

The open communities of this instance and their posts. Internal and hidden ones cannot be reached with a key.

GET/api/v2/communities

The open communities of this instance.

  • Right read
  • with ETag
  • since v2.0

Sorted by the number of members, not by time, so this list is not paged, and next is always null.

Parameters

NameTypeMeaning
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/communities?limit=20"

Answer

Answers with a page of Community, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
      "name": "Urban Gardening",
      "description": "Balconies, allotments, rooftops: whatever grows.",
      "tags": [
        "garden",
        "bees"
      ],
      "members": 312,
      "joinPolicy": "open",
      "visibility": "open"
    }
  ],
  "next": null
}

Possible statuses: 200304401429

GET/api/v2/communities/{id}

A single community, by its id.

  • Right read
  • with ETag
  • since v2.0

Parameters

NameTypeMeaning
idin the pathstringrequiredThe id of a community.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/communities/c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60"

Answer

Answers with one Community.

Example
{
  "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
  "name": "Urban Gardening",
  "description": "Balconies, allotments, rooftops: whatever grows.",
  "tags": [
    "garden",
    "bees"
  ],
  "members": 312,
  "joinPolicy": "open",
  "visibility": "open"
}

Possible statuses: 200304400401404429

GET/api/v2/communities/{id}/posts

The public posts from one community, newest first.

  • Right read
  • pageable
  • with ETag
  • since v2.0

On the public paths reactions are not counted: counts.likes and counts.reposts are 0 and pinned is false (a cut, not a measurement).

Parameters

NameTypeMeaning
idin the pathstringrequiredThe id of a community.
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/communities/c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60/posts?limit=20"

Answer

Answers with a page of Post, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "text": "The bees are back in the garden 🐝 #garden",
      "kind": "poll",
      "author": {
        "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
        "handle": "mara",
        "name": "Mara 🌻",
        "verified": true,
        "accountKind": "person"
      },
      "community": {
        "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
        "name": "Urban Gardening"
      },
      "media": [
        {
          "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
        }
      ],
      "poll": {
        "options": [
          {
            "text": "Lavender",
            "votes": 12
          },
          {
            "text": "Sunflowers",
            "votes": 7
          },
          {
            "text": "Clover",
            "votes": 3
          }
        ],
        "total": 22,
        "multiple": false,
        "endsAt": 1758710400000,
        "running": true,
        "resultsVisible": true,
        "myVotes": [
          0
        ]
      },
      "quotes": null,
      "continues": null,
      "counts": {
        "replies": 4,
        "likes": 31,
        "reposts": 2
      },
      "pinned": false,
      "createdAt": 1758624000000
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200304400401404429

Discover

What people are talking about, a search across posts, profiles and tags, and the instance’s own emojis.

GET/api/v2/tags

The tags that are currently running.

  • Right read
  • with ETag
  • since v2.0

The tags running right now, most posts first. Not paged: limit only shortens the list.

Parameters

NameTypeMeaning
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/tags?limit=10"

Answer

Answers with a page of Tag, as `items` and `next`.

Example
{
  "items": [
    {
      "tag": "garden",
      "posts": 58
    }
  ],
  "next": null
}

Possible statuses: 200304401429

GET/api/v2/search

A search over posts, names, short names and tags.

  • Right read
  • with ETag
  • since v2.0

Searches only what is public: posts outside communities, profiles and tags. limit counts per kind: 20 can bring up to 20 posts, 20 profiles and 20 tags.

Parameters

NameTypeMeaning
qin the querystringrequiredThe words being looked for.
typein the querystringoptionalWhich kind of result. Without it, every kind.allpostsprofilestags
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/search?q=garden&type=posts"

Answer

Answers with one SearchResult.

Example
{
  "posts": [
    {
      "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "text": "The bees are back in the garden 🐝 #garden",
      "kind": "poll",
      "author": {
        "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
        "handle": "mara",
        "name": "Mara 🌻",
        "verified": true,
        "accountKind": "person"
      },
      "community": {
        "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
        "name": "Urban Gardening"
      },
      "media": [
        {
          "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
        }
      ],
      "poll": {
        "options": [
          {
            "text": "Lavender",
            "votes": 12
          },
          {
            "text": "Sunflowers",
            "votes": 7
          },
          {
            "text": "Clover",
            "votes": 3
          }
        ],
        "total": 22,
        "multiple": false,
        "endsAt": 1758710400000,
        "running": true,
        "resultsVisible": true,
        "myVotes": [
          0
        ]
      },
      "quotes": null,
      "continues": null,
      "counts": {
        "replies": 4,
        "likes": 31,
        "reposts": 2
      },
      "pinned": false,
      "createdAt": 1758624000000
    }
  ],
  "profiles": [
    {
      "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
      "handle": "mara",
      "name": "Mara 🌻",
      "verified": true,
      "accountKind": "person"
    }
  ],
  "tags": [
    {
      "tag": "garden",
      "posts": 58
    }
  ]
}

Possible statuses: 200304400401429

GET/api/v2/emojis

The instance’s own emojis, with the address of their pictures.

  • Right read
  • with ETag
  • since v2.1

In texts an emoji of this instance stands as :name:. Replace it with the picture from this list; a name that is not in it stays text: it was deleted or never existed.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/emojis"

Answer

Answers with a page of Emoji, as `items` and `next`.

Example
{
  "items": [
    {
      "name": "tellmelo",
      "url": "/api/media?id=e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b"
    }
  ],
  "next": null
}

Possible statuses: 200304401429

Your account

Your own profile and your feed, put together the way the application does it for you.

GET/api/v2/me

Your own profile, with the id the other paths expect.

  • Right read
  • with ETag
  • since v2.0

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/me"

Answer

Answers with one Profile.

Example
{
  "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
  "handle": "mara",
  "name": "Mara 🌻",
  "verified": true,
  "accountKind": "person",
  "about": "Beekeeper, allotment, too many seeds. :tellmelo:",
  "website": "https://mara-imkerei.example/",
  "websiteVerified": true,
  "followers": 148,
  "createdAt": 1750848000000
}

Possible statuses: 200304401429

GET/api/v2/feed

Your own feed, the way the application puts it together.

  • Right read
  • pageable
  • since v2.0

No ETag here: the order moves with time, so every answer is different. A list once started still stays the same to its end: next carries the moment it began.

Parameters

NameTypeMeaning
tabin the querystringoptionalWhich feed: for-you, following, latest or bookmarks. latest by default.for-youfollowinglatestbookmarks
tagin the querystringoptionalOnly posts carrying this tag. Without it, no filter.
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/feed?tab=following&limit=20"

Answer

Answers with a page of Post, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "text": "The bees are back in the garden 🐝 #garden",
      "kind": "poll",
      "author": {
        "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
        "handle": "mara",
        "name": "Mara 🌻",
        "verified": true,
        "accountKind": "person"
      },
      "community": {
        "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
        "name": "Urban Gardening"
      },
      "media": [
        {
          "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
        }
      ],
      "poll": {
        "options": [
          {
            "text": "Lavender",
            "votes": 12
          },
          {
            "text": "Sunflowers",
            "votes": 7
          },
          {
            "text": "Clover",
            "votes": 3
          }
        ],
        "total": 22,
        "multiple": false,
        "endsAt": 1758710400000,
        "running": true,
        "resultsVisible": true,
        "myVotes": [
          0
        ]
      },
      "quotes": null,
      "continues": null,
      "counts": {
        "replies": 4,
        "likes": 31,
        "reposts": 2
      },
      "pinned": false,
      "createdAt": 1758624000000
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200400401429

Notifications

What happened around your account, readable page by page, so a program can catch up on what it missed.

GET/api/v2/notifications

Your own notifications, newest first, pageable, so a program can catch up on what it missed.

  • Right read
  • pageable
  • with ETag
  • since v2.0

Reading marks nothing as read; POST /notifications/read does. Several events of the same kind about the same post are gathered into one row, and more says how many.

Parameters

NameTypeMeaning
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/notifications?limit=20"

Answer

Answers with a page of Notification, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "3f2e1d0c-9b8a-4765-8432-10fedcba9876",
      "kind": "like",
      "text": null,
      "actor": {
        "handle": "jon",
        "name": "Jon"
      },
      "postId": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "read": false,
      "more": 2,
      "createdAt": 1758624300000,
      "updatedAt": 1758624900000
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200304401429

POST/api/v2/notifications/read

Mark your notifications as read.

  • Right write
  • since v2.0

Example request

curl -X POST \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/notifications/read"

Answer

Answers with one Ok.

Example
{
  "ok": true
}

Possible statuses: 200401403429

Messages

Your inbox and your conversations. A conversation has no id of its own; it is named after the other account.

GET/api/v2/conversations

Your inbox: one row per conversation, the most recent first.

  • Right read
  • pageable
  • with ETag
  • since v2.0

Parameters

NameTypeMeaning
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/conversations?limit=20"

Answer

Answers with a page of Conversation, as `items` and `next`.

Example
{
  "items": [
    {
      "with": "d4c3b2a1-6e5f-4a7b-9c8d-1e2f3a4b5c6d",
      "handle": "jon",
      "name": "Jon",
      "excerpt": "See you on Saturday at the market!",
      "truncated": false,
      "fromMe": true,
      "lastMessageId": "7c6b5a49-3827-4165-9f0e-d1c2b3a49586",
      "unread": 0,
      "updatedAt": 1758624060000
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200304401429

GET/api/v2/conversations/{with}/messages

The messages of one conversation, newest first.

  • Right read
  • pageable
  • with ETag
  • since v2.0

Reading marks nothing as read: a program fetching in the background has not shown anything to anybody. POST /conversations/{with}/read does.

Parameters

NameTypeMeaning
within the pathstringrequiredThe id of the account you are talking with. A conversation has no id of its own: it is the other account, the same value the inbox calls with.
limitin the queryintegeroptionalHow many entries a page holds: 20 by default, 50 at most.
cursorin the querystringoptionalWhere to continue: the next of the previous answer. Without it, from the top.

Example request

curl \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/conversations/a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05/messages?limit=20"

Answer

Answers with a page of Message, as `items` and `next`.

Example
{
  "items": [
    {
      "id": "7c6b5a49-3827-4165-9f0e-d1c2b3a49586",
      "text": "See you on Saturday at the market!",
      "from": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
      "to": "d4c3b2a1-6e5f-4a7b-9c8d-1e2f3a4b5c6d",
      "read": true,
      "media": [],
      "replyTo": null,
      "createdAt": 1758624060000
    }
  ],
  "next": "MTc1ODYyNDAwMDAwMDo1ZjBjMmE5ZS04ZDQx"
}

Possible statuses: 200304400401404429

POST/api/v2/conversations/{with}/messages

Send a message in a conversation.

  • Right write
  • since v2.0

The same rules as in the application apply: whoever blocked you cannot be written to, and the other person’s settings count. The answer then says why.

Images cannot be attached through the API yet, only in the application.

Parameters

NameTypeMeaning
within the pathstringrequiredThe id of the account you are talking with. A conversation has no id of its own: it is the other account, the same value the inbox calls with.
textin the bodystringrequiredThe text itself: of the post, the reply or the message.
replyToin the bodystringoptionalThe id of an earlier message in this conversation that this one answers. Since 2.2.

Example request

curl -X POST \
  -H "Authorization: Bearer tm_key_…" \
  -H "Content-Type: application/json" \
  -d '{"text":"See you on Saturday at the market!"}' \
  "https://tellmelo.com/api/v2/conversations/a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05/messages"

Answer

Answers with one Ok.

Example
{
  "ok": true
}

Possible statuses: 200400401403404429

POST/api/v2/conversations/{with}/read

Mark every message of a conversation as read.

  • Right write
  • since v2.1

Parameters

NameTypeMeaning
within the pathstringrequiredThe id of the account you are talking with. A conversation has no id of its own: it is the other account, the same value the inbox calls with.

Example request

curl -X POST \
  -H "Authorization: Bearer tm_key_…" \
  "https://tellmelo.com/api/v2/conversations/a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05/read"

Answer

Answers with one Ok.

Example
{
  "ok": true
}

Possible statuses: 200400401403404429

Relations

Like, save, repost, follow, join and block: one path for all of them, set and taken back with active.

POST/api/v2/relations

Like, save, repost, follow, join, block, mute, depending on kind.

  • Right write
  • since v2.0

target is a post for like, save and repost, a community for join, an account for follow, block and mute, always by its id. active: false takes the relation back. Nobody can follow across a block, in either direction. Only the one who mutes can see a mute.

Parameters

NameTypeMeaning
kindin the bodystringrequiredWhich relation: like, save, repost, follow, join, block or mute (since 2.2).likesaverepostfollowjoinblockmute
targetin the bodystringrequiredWhat the relation points at, by its id: a post, a profile or a community, depending on kind.
activein the bodybooleanoptionalWhether the relation should stand: true sets it, false takes it back.

Example request

curl -X POST \
  -H "Authorization: Bearer tm_key_…" \
  -H "Content-Type: application/json" \
  -d '{"kind":"like","target":"5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1","active":true}' \
  "https://tellmelo.com/api/v2/relations"

Answer

Answers with one Ok.

Example
{
  "ok": true
}

Possible statuses: 200400401403429

Objects

Everything an answer can contain, field by field. Every field is always present; the types are JSON types, and [] means a list.

Service

The answer of the root: who is answering, and what this key may do.

FieldTypeMeaning
namestringAlways tellmelo.
versionstringThe version of the API, for example 2.1.
scopestringWhat this key may do: read or read write.
rateLimitRateLimitThe budget of this key.
docsstringWhere this documentation is, as a path on this instance.
specstringWhere the machine-readable description is.
Example
{
  "name": "tellmelo",
  "version": "2.6",
  "scope": "read write",
  "rateLimit": {
    "limit": 120,
    "remaining": 117
  },
  "docs": "/legal/api",
  "spec": "/api/v2/openapi.json"
}

RateLimit

The budget of this key in the current minute.

FieldTypeMeaning
limitintegerRequests per minute.
remainingintegerHow many are left in this minute.
Example
{
  "limit": 120,
  "remaining": 117
}

Post

A post: the same shape everywhere, whether public, from your feed or from a search.

FieldTypeMeaning
idstringThe id of the post.
textstringor nullThe text as written (null for a post that is only a poll or only pictures).
kindstringWhat the post is.
  • post — A post with text, pictures or a quote.
  • poll — A post with a poll.
authorProfileBriefWho wrote it.
communityCommunityBriefor nullThe community it was written in (null outside every community).
mediaMedia[]Its images, in order; empty if there are none.
pollPollor nullThe poll (null if there is none).
quotesstringor nullThe id of the post this one quotes.
continuesstringor nullThe id of the post this one continues, as an addendum.
countsCountsReplies, likes and reposts.
pinnedbooleanWhether it is pinned to the top of its author’s profile.
createdAtintegerWhen it was written.
Example
{
  "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
  "text": "The bees are back in the garden 🐝 #garden",
  "kind": "poll",
  "author": {
    "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
    "handle": "mara",
    "name": "Mara 🌻",
    "verified": true,
    "accountKind": "person"
  },
  "community": {
    "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
    "name": "Urban Gardening"
  },
  "media": [
    {
      "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
    }
  ],
  "poll": {
    "options": [
      {
        "text": "Lavender",
        "votes": 12
      },
      {
        "text": "Sunflowers",
        "votes": 7
      },
      {
        "text": "Clover",
        "votes": 3
      }
    ],
    "total": 22,
    "multiple": false,
    "endsAt": 1758710400000,
    "running": true,
    "resultsVisible": true,
    "myVotes": [
      0
    ]
  },
  "quotes": null,
  "continues": null,
  "counts": {
    "replies": 4,
    "likes": 31,
    "reposts": 2
  },
  "pinned": false,
  "createdAt": 1758624000000
}

Reply

A reply under a post: a post with two more fields.

Every field of Post, and in addition:

FieldTypeMeaning
postIdstringThe post the whole conversation hangs off.
parentIdstringor nullThe reply this one answers (null if it answers the post directly).
Example
{
  "id": "9e4b1c7d-2a3f-4d5e-8b6c-0f1e2d3c4b5a",
  "text": "Same here, the lavender is full of them.",
  "kind": "post",
  "author": {
    "id": "d4c3b2a1-6e5f-4a7b-9c8d-1e2f3a4b5c6d",
    "handle": "jon",
    "name": "Jon",
    "verified": false,
    "accountKind": "person"
  },
  "community": null,
  "media": [],
  "poll": null,
  "quotes": null,
  "continues": null,
  "counts": {
    "replies": 0,
    "likes": 3,
    "reposts": 0
  },
  "pinned": false,
  "createdAt": 1758624600000,
  "postId": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
  "parentId": null
}

Counts

How many replies, likes and reposts a post has.

FieldTypeMeaning
repliesintegerReplies, all levels together.
likesintegerLikes.
repostsintegerReposts.
Example
{
  "replies": 4,
  "likes": 31,
  "reposts": 2
}

Media

An image of a post or a message.

FieldTypeMeaning
urlstringThe address of the picture, relative to this instance. Load it with the key in the header.
Example
{
  "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
}

Poll

The poll of a post.

FieldTypeMeaning
optionsPollOption[]The answers, in order. option in a vote counts from 0.
totalintegerHow many votes there are in total: always, even while the split is held back.
multiplebooleanWhether more than one answer may be chosen.
endsAtintegeror nullWhen the poll ends (null if it runs without an end).
runningbooleanWhether votes can still be cast.
resultsVisiblebooleanWhether the votes per answer are shown (see votes).
myVotesinteger[]The answers you chose, counted from 0.
Example
{
  "options": [
    {
      "text": "Lavender",
      "votes": 12
    },
    {
      "text": "Sunflowers",
      "votes": 7
    },
    {
      "text": "Clover",
      "votes": 3
    }
  ],
  "total": 22,
  "multiple": false,
  "endsAt": 1758710400000,
  "running": true,
  "resultsVisible": true,
  "myVotes": [
    0
  ]
}

PollOption

One answer of a poll.

FieldTypeMeaning
textstringThe answer.
votesintegeror nullIts votes (null while the results are held back).
Example
{
  "text": "Lavender",
  "votes": 12
}

ProfileBrief

A profile as it appears inside other objects: as an author, in a search.

FieldTypeMeaning
idstringThe id of the account: what /relations and /conversations/{with} expect.
handlestringThe short name, without @. It is part of the profile address and cannot contain emojis.
namestringThe display name. It may contain emojis, including :name:.
verifiedbooleanWhether the account is verified.
accountKindstringWhat kind of account it is.
  • person — A person.
  • business — A business.
  • association — A club or association.
  • automated — An automated account, such as a bot.
Example
{
  "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
  "handle": "mara",
  "name": "Mara 🌻",
  "verified": true,
  "accountKind": "person"
}

Profile

A profile on its own, with its description and its followers.

Every field of ProfileBrief, and in addition:

FieldTypeMeaning
aboutstringor nullThe description (null if there is none).
websitestringor nullThe profile’s website (null if there is none). Since 2.3.
websiteVerifiedbooleanWhether the website links back to this profile with rel=me, last checked within a week. Since 2.3.
followersintegerHow many accounts follow it.
createdAtintegerWhen the account was created.
Example
{
  "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
  "handle": "mara",
  "name": "Mara 🌻",
  "verified": true,
  "accountKind": "person",
  "about": "Beekeeper, allotment, too many seeds. :tellmelo:",
  "website": "https://mara-imkerei.example/",
  "websiteVerified": true,
  "followers": 148,
  "createdAt": 1750848000000
}

CommunityBrief

The community a post was written in.

FieldTypeMeaning
idstringThe id of the community.
namestringor nullIts name.
Example
{
  "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
  "name": "Urban Gardening"
}

Community

A community of this instance.

FieldTypeMeaning
idstringThe id of the community.
namestringIts name.
descriptionstringor nullThe description (null if there is none).
tagsstring[]The topics it is about.
membersintegerHow many members it has.
joinPolicystringHow one gets in.
  • open — Anyone can join.
  • application — Joining takes an application that the community accepts.
  • invite — Only by invitation.
visibilitystringWho can see it. Through a key always open: the others cannot be reached.
  • open — Visible to everyone.
Example
{
  "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
  "name": "Urban Gardening",
  "description": "Balconies, allotments, rooftops: whatever grows.",
  "tags": [
    "garden",
    "bees"
  ],
  "members": 312,
  "joinPolicy": "open",
  "visibility": "open"
}

Tag

A tag and how many posts carry it.

FieldTypeMeaning
tagstringThe tag, without #.
postsintegerHow many recent posts carry it.
Example
{
  "tag": "garden",
  "posts": 58
}

SearchResult

What a search finds: three lists, each possibly empty, never missing.

FieldTypeMeaning
postsPost[]Matching posts, newest first.
profilesProfileBrief[]Matching profiles, by short name.
tagsTag[]Matching tags, most used first.
Example
{
  "posts": [
    {
      "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
      "text": "The bees are back in the garden 🐝 #garden",
      "kind": "poll",
      "author": {
        "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
        "handle": "mara",
        "name": "Mara 🌻",
        "verified": true,
        "accountKind": "person"
      },
      "community": {
        "id": "c81d4e2a-0f3b-4a6c-9e57-1d2b3c4a5f60",
        "name": "Urban Gardening"
      },
      "media": [
        {
          "url": "/api/media?id=b2d7e9f1-3c4a-4e8b-a1f0-6d5c4b3a2918"
        }
      ],
      "poll": {
        "options": [
          {
            "text": "Lavender",
            "votes": 12
          },
          {
            "text": "Sunflowers",
            "votes": 7
          },
          {
            "text": "Clover",
            "votes": 3
          }
        ],
        "total": 22,
        "multiple": false,
        "endsAt": 1758710400000,
        "running": true,
        "resultsVisible": true,
        "myVotes": [
          0
        ]
      },
      "quotes": null,
      "continues": null,
      "counts": {
        "replies": 4,
        "likes": 31,
        "reposts": 2
      },
      "pinned": false,
      "createdAt": 1758624000000
    }
  ],
  "profiles": [
    {
      "id": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
      "handle": "mara",
      "name": "Mara 🌻",
      "verified": true,
      "accountKind": "person"
    }
  ],
  "tags": [
    {
      "tag": "garden",
      "posts": 58
    }
  ]
}

Emoji

An emoji of this instance.

FieldTypeMeaning
namestringThe name, as it stands between the colons.
urlstringThe address of the picture, relative to this instance.
Example
{
  "name": "tellmelo",
  "url": "/api/media?id=e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b"
}

Notification

A notification. One row can gather several events of the same kind.

FieldTypeMeaning
idstringThe id of the notification.
kindstringWhat happened.
  • reply — Someone replied to you.
  • like — Someone likes your post.
  • repost — Someone reposted your post.
  • follow — Someone follows you.
  • mention — Someone mentioned you.
  • group_mention — Someone mentioned a group you lead; text is its name.
  • message — Someone wrote you a message.
  • scheduled — A scheduled post of yours has been published.
  • reminder — A reminder about a saved post is due.
  • report — What became of a report you sent in.
  • moderation — A decision about your account: a warning, a restriction, an appeal.
  • team — New work for the team, only for owners, admins and moderators: reports, appeals, submitted links, inquiries, verification requests and cancellations.
textstringor nullOnly for reports and moderation: the text that comes with it. Otherwise null: the sentence for a notification is built by your program.
actorActorWho did it.
postIdstringor nullThe post it is about (null if it is about none).
readbooleanWhether it has been marked as read.
moreintegerHow many further events this row stands for, beyond the one named.
createdAtintegerWhen it first happened.
updatedAtintegerWhen it last gathered another event. The list is sorted by this.
Example
{
  "id": "3f2e1d0c-9b8a-4765-8432-10fedcba9876",
  "kind": "like",
  "text": null,
  "actor": {
    "handle": "jon",
    "name": "Jon"
  },
  "postId": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
  "read": false,
  "more": 2,
  "createdAt": 1758624300000,
  "updatedAt": 1758624900000
}

Actor

Who triggered a notification.

FieldTypeMeaning
handlestringThe short name.
namestringThe display name.
Example
{
  "handle": "jon",
  "name": "Jon"
}

Conversation

One row of your inbox.

FieldTypeMeaning
withstringThe id of the other account: what /conversations/{with}/messages expects.
handlestringTheir short name.
namestringTheir display name.
excerptstringor nullThe beginning of the last message (null if it has no text).
truncatedbooleanWhether the excerpt was cut short.
fromMebooleanWhether the last message is yours.
lastMessageIdstringThe id of the last message.
unreadintegerHow many of their messages you have not read yet.
updatedAtintegerWhen the last message was written.
Example
{
  "with": "d4c3b2a1-6e5f-4a7b-9c8d-1e2f3a4b5c6d",
  "handle": "jon",
  "name": "Jon",
  "excerpt": "See you on Saturday at the market!",
  "truncated": false,
  "fromMe": true,
  "lastMessageId": "7c6b5a49-3827-4165-9f0e-d1c2b3a49586",
  "unread": 0,
  "updatedAt": 1758624060000
}

Message

A message in a conversation.

FieldTypeMeaning
idstringThe id of the message.
textstringor nullThe text (null for a message that is only pictures).
fromstringThe id of the account that wrote it.
tostringThe id of the account it was written to.
readbooleanWhether the recipient has read it.
mediaMedia[]Its images; empty if there are none.
replyTostringor nullThe id of the message this one answers (null if it answers none). Since 2.2.
createdAtintegerWhen it was sent.
Example
{
  "id": "7c6b5a49-3827-4165-9f0e-d1c2b3a49586",
  "text": "See you on Saturday at the market!",
  "from": "a3e1f7c2-54b9-4d0e-8f16-7b2c9d4e1a05",
  "to": "d4c3b2a1-6e5f-4a7b-9c8d-1e2f3a4b5c6d",
  "read": true,
  "media": [],
  "replyTo": null,
  "createdAt": 1758624060000
}

Created

The answer of a write that created something.

FieldTypeMeaning
idstringThe id of what was created.
scheduledForintegeror nullWhen it appears, if publishing was scheduled (otherwise null).
deleteAtintegeror nullWhen it deletes itself, if that was set (otherwise null).
Example
{
  "id": "5f0c2a9e-8d41-4b7a-9c3e-2e61d0f4a7b1",
  "scheduledFor": null,
  "deleteAt": null
}

Ok

The answer of a write that has nothing to hand back.

FieldTypeMeaning
okbooleanAlways true.
Example
{
  "ok": true
}

The machine-readable description

The same table this page is built from is served at /api/v2/openapi.json: paths, parameters, rights and the shapes that come back. A client generator can read it, and it cannot drift away from the API, because the page, the router and the description all come from one list.

https://tellmelo.com/api/v2/openapi.json

What became of v1

v1 has been removed. The old paths under /api/v1/ answer 410 and say in the body where to go instead. No redirect, because v2 answers in a different shape, and a program that followed one would get a 200 it cannot read. There were no public accounts yet at that point, and so nobody with a program on it; removing it later would have meant never removing it.

What we expect

The same guidelines as everywhere else: no harassment, no spam, no one else’s content without the right to it. A program excuses nothing: you answer for what your key writes.

Back to tellmelo