Code examples

Please see the API spec for more detailed technical usage, this page is just designed to help you get started as quickly as possible in several common languages.

Ping

To check that your connection is working, we can ping the server. By default, responses are returned as JSON:

curl -H "Authorization: Bearer <YourAccessToken>" \
    https://api.datathistle.com/v1/ping
import axios from 'axios';

const accessToken = '<YourAccessToken>';
const response = await axios.get(`https://api.datathistle.com/v1/ping`, {
    headers: { Authorization: `Bearer ${accessToken}` }
});
const data = response.data;
const accessToken = '<YourAccessToken>';
const response = await fetch('https://api.datathistle.com/v1/ping', {
    headers: { Authorization: `Bearer ${accessToken}` }
});
const data = await response.json();
$accessToken = '<YourAccessToken>';

// Guzzle
use GuzzleHttp\Client;

$client   = new Client();
$response = $httpClient->get('https://api.datathistle.com/v1/ping', [
    'headers' => ['Authorization' => 'Bearer '. $accessToken]
]);
$data = json_decode($response, true);

// cURL
$ch = curl_init('https://api.datathistle.com/v1/ping');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer '.$accessToken]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
import requests

accessToken = '<YourAccessToken>';
response    = requests.get('https://api.datathistle.com/v1/ping', headers={'Authorization': f'Bearer {accessToken}'})
data        = response.json()
<cfset accessToken="<YourAccessToken>" />

<cfhttp method="GET" url="https://api.datathistle.com/v1/ping">
	<cfhttpparam type="header" name="Authorization" value="Bearer #accessToken#" />
</cfhttp>

<cfset data = DeserializeJSON(cfhttp.fileContent) />
accessToken = "<YourAccessToken>";

cfhttp(method="GET", url="https://api.datathistle.com/v1/ping") {
	cfhttpparam(type="header", name="Authorization", value="Bearer #accessToken#");
}

data = DeserializeJSON(cfhttp.fileContent);
{
	"status": 200,
	"message": "pong!",
	"query": [],
	"content-type": null
}

Different return types

If you don't use JSON (application/json), the API also supports: YAML (application/yaml), TOML (application/toml), XML (application/xml) and FORM (application/x-www-form-urlencoded) encodings. You can specify the input type with the "Content-Type" header, and the output type with the "Accept" header. These do not need to match. The method can also be either POST or GET. The "ping" endpoint will output any variables passed to it, so to send an input as XML, but output as YAML:

curl -X POST \
    -H "Authorization: Bearer <YourAccessToken>" \
    -H "Content-Type: application/xml" \
    -H "Accept: application/yaml" \
    -d "<data><date>2025-09-01</date><time>08:30</time></data>" \
    https://api.datathistle.com/v1/ping
import axios from 'axios';
import yaml  from 'js-yaml';

const accessToken = '<YourAccessToken>';
const response    = await axios.post(
    `https://api.datathistle.com/v1/ping`,
    '<data><date>2025-09-01</date><time>08:30</time></data>',
    {
        headers: {
            Authorization:  `Bearer ${accessToken}`,
            'Content-Type': 'text/xml',
            Accept:         'application/yaml',
        }
    }
);
const data = yaml.load(response.data);
const accessToken = '<YourAccessToken>';
const response    = await fetch('https://api.datathistle.com/v1/ping', {
    method:  'POST',
    body:    '<data><date>2025-09-01</date><time>08:30</time></data>',
    headers: {
        Authorization:  `Bearer ${accessToken}`,
        'Content-Type': 'text/xml',
        Accept:         'application/yaml',
    }
});
const blob       = await response.blob();
const yamlString = await blob.text();
$accessToken = '<YourAccessToken>';

// Guzzle
use GuzzleHttp\Client;

$client   = new Client();
$response = $httpClient->post('https://api.datathistle.com/v1/ping', [
    'headers' => [
        'Content-Type'  => 'application/xml',
        'Accept'        => 'application/yaml',
        'Authorization' => 'Bearer '.$accessToken,
    ]
]);
$data = yaml_parse($response);

// cURL
$ch = curl_init('https://api.datathistle.com/v1/ping');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type:  application/xml',
    'Accept:        application/yaml',
    'Authorization: Bearer '.$accessToken,
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, '<data><date>2025-09-01</date><time>08:30</time></data>');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = yaml_parse($response);
import requests
import yaml

accessToken = '<YourAccessToken>';
response    = requests.post('https://api.datathistle.com/v1/ping', data='<data><date>2025-09-01</date><time>08:30</time></data>', headers={
    'Content-Type':  'application/xml',
    'Accept':        'application/yaml',
    'Authorization': f'Bearer {accessToken}',
})
data = yaml.safe_load(response.text())
javaloader = createObject("component", "javaloader.JavaLoader").init(expandPath("./jyaml-1.3.jar"));
yaml       = javaloader.create("org.ho.yaml.Yaml");

<cfset accessToken="<YourAccessToken>" />

<cfhttp method="GET" url="https://api.datathistle.com/v1/ping">
	<cfhttpparam type="header" name="Authorization" value="Bearer #accessToken#" />
</cfhttp>

<cfset data = yaml.load(cfhttp.fileContent) />
javaloader = createObject("component", "javaloader.JavaLoader").init(expandPath("./jyaml-1.3.jar"));
yaml       = javaloader.create("org.ho.yaml.Yaml");

accessToken = "<YourAccessToken>";

cfhttp(method="GET", url="https://api.datathistle.com/v1/ping") {
	cfhttpparam(type="header", name="Authorization", value="Bearer #accessToken#");
}

data = yaml.load(cfhttp.fileContent);
---
status: 200
message: pong!
query:
  date: "2025-09-01"
  time: 08:30
content-type: application/yaml
...

Pagination

Several endpoints will only return a limited number of results at a time (`/events`, '/places`, `/search`). These results may be paginated using either the standard HTTP "Link" header, or the simpler "X-Next" header:

import axios from 'axios';

const accessToken = '<YourAccessToken>';
const events      = [];
const getEvents   = async (url) => {
	const response = await axios.get(url, {
        headers: { Authorization: `Bearer ${accessToken}` }
    });

    events.concat(response.data);

    if (response.headers['x-next']) {
        await getEvents(response.headers['x-next']);
    }
};

// Fetch all events within 1 mile of Edinburgh
await getEvents('https://api.datathistle.com/v1/events?lat=55.953251&lon=-3.188267&distance=1');
const accessToken = '<YourAccessToken>';
const events      = [];
const getEvents   = async (url) => {
	const response = await fetch(url, {
        headers: { Authorization: `Bearer ${accessToken}` }
    });
    const next = response.headers.get('x-next');
    const data = await response.json();

    events.concat(data);

    if (next) {
        await getEvents(next);
    }
};

// Fetch all events within 1 mile of Edinburgh
await getEvents('https://api.datathistle.com/v1/events?lat=55.953251&lon=-3.188267&distance=1');
use GuzzleHttp\Client;

$getEvents = function(string $url, string $accessToken): array {
    $client   = new Client();
    $response = $httpClient->get($url, [
        'headers' => ['Authorization' => 'Bearer '. $accessToken]
    ]);
    $events = json_decode($response, true);

    if ($response->hasHeader('x-next')) {
        array_push($events, ...$getEvents($response->getHeader('x-next')[0], $accessToken));
    }

    return $events
}

// Fetch all events within 1 mile of Edinburgh
$events = $getEvents('https://api.datathistle.com/v1/events?lat=55.953251&lon=-3.188267&distance=1', '<YourAccessToken>');
<cfset accessToken = "<YourAccessToken>" />
<cfset events = [] />

<cffunction name="getEvents">
	<cfargument name="url" required="yes" />

	<cfhttp method="GET" url="#arguments.url#" result="response">
		<cfhttpparam type="header" name="Authorization" value="Bearer #accessToken#" />
	</cfhttp>

	<cfset events.append(DeserializeJSON(response.fileContent), true) />

	<cfif StructKeyExists(response.responseheader, "x-next")>
		<cfset getEvents(response.responseheader["x-next"]) />
	</cfif>
</cffunction>

<!--- Fetch all events within 1 mile of Edinburgh --->
<cfset getEvents("https://api.datathistle.com/v1/events?lat=55.953251&lon=-3.188267&distance=1") />
accessToken = "<YourAccessToken>";
events      = [];

function getEvents(url) {
	cfhttp(method="GET", url=arguments.url, result="response") {
		cfhttpparam(type="header", name="Authorization", value="Bearer #accessToken#");
	}

	events.append(DeserializeJSON(response.fileContent), true);

	if (StructKeyExists(response.responseheader, "x-next")) {
		getEvents(response.responseheader["x-next"]);
	}
}

// Fetch all events within 1 mile of Edinburgh
getEvents("https://api.datathistle.com/v1/events?lat=55.953251&lon=-3.188267&distance=1");