Overview
We offer two ways to access our data:
- The XMPP API (preferred)
- The anonymous HTTP API (discouraged)
If you have not noticed by now, the usage of the XMPP API is preferred. The reason is that you need to be authenticated to perform requests against the XMPP API, which makes it easier for us to work against abuse.
The HTTP API can be used anonymously, so we might rate-limit it more strictly in the future.
XMPP API
search.jabber.network offers a fully compliant Extended Channel Search (XEP-0433) API at api@search.jabber.network
.
Please refer to the linked XEP document for instructions on how to interact with the API.
There is a description of the Legacy XMPP API (pre-XEP-0433) below, for historical purposes.
HTTP API
We offer a minimalistic HTTP-based JSON API to query the room list.
Rate limiting
We offer this service as a best-effort service. If you receive a 429 status code, please wait at least one minute for your next request.
Please avoid downloading the whole list unless you have to do so. If you want to do additional filtering locally, that is a good reason.
API calls
GET /api/1.0/rooms
Description
Return the list of rooms as JSON object. The list is paginated with a constant number items per page and ordered by the address ascendingly.
Parameters
after
- Optional JID. Only MUCs whose address orders after the given JID will be returned. This is used for pagination.
min_users
- Optional integer number. Only MUCs whose number of users moving average is greater than or equal to this number will be returned.
Error conditions
400
- The
after
argument is not a valid JID.
- The
min_users
argument is not an integer.
429
- Rate limit exceeded. Please wait at least one minute before issuing the next request.
Response
The response is a JSON object formatted like this:
{
"items": [
{
"address": <(str) address of the room>,
"nusers": <(int or null) approximate number of users>,
"is_open": <(bool) whether it can be joined by anyone>,
"name": <(str or null) name of the room>,
"description": <(str or null) description of the room>,
"language": <(str or null) primary language of the room>
},
< ... >
]
}
The number of items per page is a service constant.
To continue to the next page, send a new request with the after
argument set to the address
of the last item in the previous page. Paging backwards is not supported.
This method is intended to obtain a complete listing of the rooms. If rooms are added concurrently with the listing, they may or may not be included. If a min_users
condition is used and the number of users in the room crosses the threshold concurrently to the listing, it may or may not be included.
GET /api/1.0/rooms/unsafe
Description
Return the list of rooms as JSON object. The list is paginated with a (fairly) constant number items per page and ordered by number of users descendingly.
Parameters
p
- Required. The page of the listing. The first page is page 1.
order_by
- Optional. The column by which the results will be ordered. Allowed values: . Default:
nusers
Error conditions
400
- The page number was not given.
- The given page was not an integer.
- The given page was less than one.
- The order by column was given and not equal to
nusers
.
429
- Rate limit exceeded. Please wait at least one minute before issuing the next request.
Response
The response is a JSON object formatted like this:
{
"page": <(int) page number>,
"total": <(int) total number of items in all pages>,
"pages": <(int) total number of pages>,
"items": [
{
"address": <(str) address of the room>,
"nusers": <(int or null) approximate number of users>,
"is_open": <(bool) whether it can be joined by anyone>,
"anonymity_mode": <(str) "semi" or "none", indicating the level of anonymity ("semi" = room moderators can see occupant JIDs, "none" = everyone can)>,
"name": <(str or null) name of the room>,
"description": <(str or null) description of the room>,
"language": <(str or null) primary language of the room>
},
< ... >
]
}
The number of items per page is a service constant.
Warning: This listing is not safe against insert/reorder/delete race conditions when fetching multiple pages. This means that rooms can be missing or can be duplicated in the result.
GET /api/1.0/search
Description
Warning: This API is experimental and may change in incompatible ways without notice.
Execute a room search like on the website.
Warning: The search result is not safe against insert/reorder/delete race conditions when fetching multiple pages. This means that rooms can be missing or can be duplicated in the result.
Note: The use of the POST method for searching is supported, but deprecated.
Payload
The payload must be JSON-formatted string. It must be sent with Content-Type: application/json
and be encoded for the wire using UTF-8.
The object can have the following keys:
keywords
- Required. Either a string (which will be tokenized in an unspecified way like the web UI tokenizes it) or a list of keywords.
sinaddr
- Optional boolean. If true, the search will take the address of the rooms into account. Defaults to true.
sindescr
- Optional boolean. If true, the search will take the description of the rooms into account. Defaults to true.
sinname
- Optional boolean. If true, the search will take the name of the rooms into account. Defaults to true.
min_users
- Optional float. The minimum number of users a room must have to be included in the result. Defaults to 0.
after
- Optional. Pagination key. See below for details.
Error conditions
400
- Too many keywords in request
- Invalid type for
keywords
, min_users
or after
- Search scope is empty: none of the boolean flags for where to search evaluated to true.
keywords
is missing.
429
- Rate limit exceeded. Please wait at least one minute before issuing the next request.
Pagination
The API will only return a limited number of items. This is at least 10 items. The actual number may vary with server load or at the discretion of the service provider.
To request the next page of results, the value of the last
key in the result
key of the response object must be passed as after
in the subsequent request.
Response
The response is a JSON object formatted like this:
{
"query": {
<the expanded search request>
},
"result": {
"last": <opaque pagination key (see above)>,
"more": <(bool) flag which indicates that there may be more data available on a subsequent page>,
"items": [
{
"address": <(str) address of the room>,
"nusers": <(int or null) approximate number of users>,
"is_open": <(bool) whether it can be joined by anyone>,
"anonymity_mode": <(str or null) "semi" or "none", indicating the level of anonymity ("semi" = room moderators can see occupant JIDs, "none" = everyone can). May be null if the service currently lacks data.>,
"name": <(str or null) name of the room>,
"description": <(str or null) description of the room>,
"language": <(str or null) primary language of the room>
},
< ... >
]
}
}
Example
$ curl -s \
--data '{"keywords": ["xsf"]}' \
-H 'Content-Type: application/json; charset=utf-8' \
https://search.jabber.network/api/1.0/search | jq -C .
{
"query": {
"after": null,
"keywords": [
"xsf"
],
"min_users": 0,
"sinaddr": true,
"sindescr": true,
"sinname": true
},
"result": {
"items": [
{
"address": "xsf@muc.xmpp.org",
"anonymity_mode": "semi",
"description": "Discussion room of the XMPP Standards Foundation",
"is_open": true,
"language": "en",
"name": "XSF Discussion",
"nusers": 66
},
{
"address": "council@muc.xmpp.org",
"anonymity_mode": "semi",
"description": "Room where the XSF XMPP Council holds its meetings",
"is_open": true,
"language": "en",
"name": "XMPP Council",
"nusers": 23
},
{
"address": "commteam@muc.xmpp.org",
"anonymity_mode": "semi",
"description": null,
"is_open": true,
"language": "en",
"name": "XSF Communications Team",
"nusers": 14
},
{
"address": "editor@muc.xmpp.org",
"anonymity_mode": "semi",
"description": "Discussion room for the XSF Editor Team",
"is_open": true,
"language": "en",
"name": "XSF Editor Team",
"nusers": 12
}
],
"last": 12.1112915471192,
"more": false
}
}
GET /api/1.0/badge
Description
Return an SVG image with the room name (or address, if the name is unavailable) and the number of users as shown on the listing.
Parameters
address
- Required. The address of the room.
Error conditions
400
- The address was not given.
404
- No room with the given address found.
429
- Rate limit exceeded. Please wait at least one minute before issuing the next request.
Response
The response is an SVG image.
Example
<img src="https://search.jabber.network/api/1.0/badge?address=xsf@muc.xmpp.org"/>
Legacy XMPP API
The Legacy XMPP API is based on XEP-0004 (Data Forms) and XEP-0059 (Result Set Management) (don’t worry: the results are not in a XEP-0004 report!).
The search parameters are sent via a XEP-0004 form. The results are in a custom wire format which is paginated using XEP-0059 RSM.
The XMPP API endpoint is api@search.jabber.network
.
Obtaining the search parameters form
To request the search form, send the following request:
<iq to="api@search.jabber.network" type="get">
<search xmlns="https://xmlns.zombofant.net/muclumbus/search/1.0"/>
</iq>
Reminder: You have to set a unique id
attribute on the IQ.
Note: You may find it weird that the to
address is a bare JID. This is intended and correct. api@search.jabber.network
is in fact a proxy service which delegates the query to one of possibly multiple workers.
The service will respond with the form (see below for the full form type specification):
<iq type='result' from='api@search.jabber.network'>
<search xmlns='https://xmlns.zombofant.net/muclumbus/search/1.0'>
<x xmlns='jabber:x:data' type='form'>
<field type='hidden' var='FORM_TYPE'>
<value>https://xmlns.zombofant.net/muclumbus/search/1.0#params</value>
</field>
<field type='text-single' var='q' label='Search for'>
</field>
<field type='boolean' var='sinname' label='Search in name'>
<value>true</value>
</field>
<field type='boolean' var='sindescription' label='Search in description'>
<value>true</value>
</field>
<field type='boolean' var='sinaddr' label='Search in address'>
<value>true</value>
</field>
<field type='text-single' var='min_users' label='Minimum number of users'>
<value>1</value>
</field>
<field type='list-single' var='key' label='Sort results by'>
<value>nusers</value>
<option label='Number of online users'><value>nusers</value></option>
<option label='Address'><value>address</value></option>
</field>
</x>
</search>
</iq>
Search parameters
q
- Optional string. Operates like the search box on the website. If left blank, keyword search is disabled altogether and a full result is returned.
sinname
- Optional boolean (default: true). Flag to indicate that the search should search within the MUC names.
sindescription
- Optional boolean (default: true). Flag to indicate that the search should search within the MUC descriptions.
sinaddr
- Optional boolean (default: true). Flag to indicate that the search should search within the MUC addresess.
min_users
- Optional integer (default: 1). Minimum number of users in the MUCs to return.
key
- The key of the result set management and the field by which the results are ordered. See below for details.
The key
field specifies the order in which results are returned and also the field which is used for XEP-0059 pagination. Two values are allowed:
address
- Order ascendingly by the address of the MUC. Paginating through this result will provide a duplicate-free but not necessarily complete view of the database (with respect to the keyword search and min users parameters). If a MUC is updated to match the search criteria or added while the pagination happens it may or may not be included in the complete result set.
nusers
- Order descendingly by the number of users in the MUC. Paginating through the result may have duplicates and may not include all items.
Executing the search
To execute the search, the filled-out search form must be sent. For example, to search for xmpp.org
in ascending order of the address with a page size of 5 and only returning MUCs which have at least one user, use:
<iq to="api@search.jabber.network" type="get">
<search xmlns="https://xmlns.zombofant.net/muclumbus/search/1.0">
<set xmlns="http://jabber.org/protocol/rsm">
<max>5</max>
</set>
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden">
<value>https://xmlns.zombofant.net/muclumbus/search/1.0#params</value>
</field>
<field var="q" type="text-single" label="Search for">
<value>xmpp.org</value>
</field>
<field var="sinname" type="boolean" label="Search in name">
<value>true</value>
</field>
<field var="sindescription" type="boolean" label="Search in description">
<value>true</value>
</field>
<field var="sinaddr" type="boolean" label="Search in address">
<value>true</value>
</field>
<field var="min_users" type="text-single" label="Minimum number of users">
<value>1</value>
</field>
<field var="key" type="list-single" label="Sort results by">
<value>address</value>
<option label="Number of online users"><value>nusers</value></option>
<option label="Address"><value>address</value></option>
</field>
</x>
</search>
</iq>
The XEP-0059 element is optional in the initial search request. It MAY be used to configure the page size. Note that the service may override your choice.
The server will then respond with the first page of the result set:
<iq type='result' from='api@search.jabber.network'>
<result xmlns='https://xmlns.zombofant.net/muclumbus/search/1.0'>
<item address='commteam@muc.xmpp.org'>
<name>commteam</name>
<nusers>10</nusers>
<is-open/>
</item>
[…]
<item address='operators@muc.xmpp.org'>
<name>XMPP Service Operators</name>
<description>Discussion venue for operators of federated XMPP services</description>
<nusers>43</nusers>
<is-open/>
</item>
<set xmlns='http://jabber.org/protocol/rsm'>
<first>opaque-string-1</first>
<last>opaque-string-2</last>
<max>5</max>
</set>
</result>
</iq>
Note: The child element of the iq
is a different one than used in the request!
The result contains of a sequence of items and a XEP-0059 element. The latter is used for pagination. The <max/>
child is always included; it indicates the page size chosen by the service. If the number of items returned is less than the page size, you have reached the last page and should not continue to paginate further.
The result <item/>
elements have the following syntax:
- @
address
- This attribute carries the address of the result MUC.
<name/>
- Optional string. The name of the MUC. May be omitted if unknown.
<description/>
- Optional string. The description of the MUC. May be omitted if unknown.
<language/>
- Optional string. The language of the MUC. May be omitted if unknown.
<is-open/>
- Optional. Does not have children or text. Its presence indicates that the MUC is open to join by anyone.
<anonymity-mode/>
- Optional string. Anonymity mode of the MUC. Either
semi
or none
. semi
indicates that only room moderators can see the occupants real JIDs. none
indicates that everyone can see everyones JIDs.
<nusers>
- Integer. Approximate number of users in the room.
Note: more fields may be added in the future without notice or changing the namespace.
Pagination
To paginate through the results, send the same form again and set the <after/> element of the XEP-0059 element to the value given in the <last/> element of the last page your received:
<iq to="api@search.jabber.network" id="xfdrffYzW2FTxdE2i6+0W" type="get">
<search xmlns="https://xmlns.zombofant.net/muclumbus/search/1.0">
<set xmlns="http://jabber.org/protocol/rsm">
<after>opaque-string-2</after>
<max>5</max>
</set>
<x xmlns="jabber:x:data" type="submit">
<!-- Your search goes here -->
</x>
</search>
</iq>
The server will then return the next page. You can repeat this until your recieve a result set which has fewer items than indicated in the <max/> element of the results RSM element.
Note: This service does not support the full XEP-0059 semantics. Paginating is only supported through the <after/> element.
Form specification
<form_type>
<name>https://xmlns.zombofant.net/muclumbus/search/1.0</name>
<doc>https://muclumbus.jabbercat.org/docs/api</doc>
<desc>
Form to search through a public MUC directory.
</desc>
<field
var='q'
type='text-single'
label='Search for'/>
<field
var='sinname'
type='boolean'
label='Search in name'/>
<field
var='sindescription'
type='boolean'
label='Search in description'/>
<field
var='sinaddr'
type='boolean'
label='Search in address'/>
<field
var='min_users'
type='text-single'
label='Minimum number of users'/>
<field
var='key'
type='list-single'
label='Sort results by'/>
</form_type>