Documentation for Developers

Overview

We offer two ways to access our data:

  1. The XMPP API (preferred)
  2. 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

The 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>

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:
  • nusers
. 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"/>

Navigation