Authentication
Create Token
POST
/
tokens
Tokens
curl --request POST \
--url https://demand.researchdesk.com/tokens \
--header 'Content-Type: application/json' \
--data '
{
"email": "{{email}}",
"password": "{{password}}"
}
'import requests
url = "https://demand.researchdesk.com/tokens"
payload = {
"email": "{{email}}",
"password": "{{password}}"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: '{{email}}', password: '{{password}}'})
};
fetch('https://demand.researchdesk.com/tokens', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://demand.researchdesk.com/tokens",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '{{email}}',
'password' => '{{password}}'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://demand.researchdesk.com/tokens"
payload := strings.NewReader("{\n \"email\": \"{{email}}\",\n \"password\": \"{{password}}\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://demand.researchdesk.com/tokens")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"{{email}}\",\n \"password\": \"{{password}}\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://demand.researchdesk.com/tokens")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"{{email}}\",\n \"password\": \"{{password}}\"\n}"
response = http.request(request)
puts response.read_body{
"token": "eyJraWQiOiJuNG93MStFaVdQeThpSVJLR2o1YXNvaURJTmNWNnJPaG9UWDNrXC9JN05Vaz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIzNDQ4OTQ0OC03MDgxLTcwYjItN2NkZC00MDEzZWU5YjkzMmQiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV80UEVLNUpncHgiLCJjbGllbnRfaWQiOiI0c2JoczN1dDMxaTE5b2lpY2lmNmlocGpuMSIsIm9yaWdpbl9qdGkiOiIyMzJjYmQ3My05YmEzLTQyM2MtYTU5YS1lYmJmMDM3MjJhZGIiLCJldmVudF9pZCI6IjA0YWI2MTEwLTA3ZGEtNDZmYy04N2MwLTY0ODAyYzgzNzZmMyIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE3Mzc0OTYxNjQsImV4cCI6MTczNzUwNjk2NCwiaWF0IjoxNzM3NDk2MTY0LCJqdGkiOiIxOTlhM2M0YS0yYTJkLTQxMjgtOWQ4Mi01YmQ3YjFjMGYwZTAiLCJ1c2VybmFtZSI6IjM0NDg5NDQ4LTcwODEtNzBiMi03Y2RkLTQwMTNlZTliOTMyZCJ9.gFI515igfGK9wNijA0fI-usEyaDatdJG5GZK0_byZoPWGQ9VoViaU1TZ_CtyI-rG6DN_-DJ3IBaUtrXbRZb8LIGRs8bvzKRdSyE3fRAsYht7NlczRRVH2w0TkgRX_8O8FZWERbOT2d3FfS_WVh5TG15QZGXmJBaTwTjKkxtQ5WZg_QGIocXfFA5j5vFOmKXEFsp_r7VzDA12BQBGaSq99li0aGLlROGY-svzskS_HUzlP3zRGuaaKv1vte0DUqC-4FMADhnSqw0pFvJcU6W5F3snuqRaHyKYD0g5Q3UBYm4fgBw-CHXz8jzB43yHU_I_X70OlOFWDjFwoONwX8-HXQ",
"expires": "2026-10-20 06:53:25"
}Rate limit: 5 requests/hour, per email address
Email and password are required to generate a JWT for later API calls. This endpoint issues tokens for both Cognito users and WorkOS users.
The user must have the Demand API user access toggle enabled. An Admin grants the toggle in Research Desk Admin settings. The Admin role on its own no longer grants a user access to the API.
Once the token is generated, you will need to pass that token in the Authorization header for subsequent API calls. The header value is the token itself. Do not add a Bearer prefix.
Tokens have a set lifespan, and the expires field is how you read it. expires is a UTC datetime string formatted YYYY-MM-DD HH:MM:SS (for example, 2026-10-20 06:53:25). The default lifespan is 12 hours in the Research Desk production environment and 24 hours in staging. The expires field is the source of truth.
It is recommended to cache the token instead of requesting a new one before every API call.
Was this page helpful?
Tokens
curl --request POST \
--url https://demand.researchdesk.com/tokens \
--header 'Content-Type: application/json' \
--data '
{
"email": "{{email}}",
"password": "{{password}}"
}
'import requests
url = "https://demand.researchdesk.com/tokens"
payload = {
"email": "{{email}}",
"password": "{{password}}"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: '{{email}}', password: '{{password}}'})
};
fetch('https://demand.researchdesk.com/tokens', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://demand.researchdesk.com/tokens",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '{{email}}',
'password' => '{{password}}'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://demand.researchdesk.com/tokens"
payload := strings.NewReader("{\n \"email\": \"{{email}}\",\n \"password\": \"{{password}}\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://demand.researchdesk.com/tokens")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"{{email}}\",\n \"password\": \"{{password}}\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://demand.researchdesk.com/tokens")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"{{email}}\",\n \"password\": \"{{password}}\"\n}"
response = http.request(request)
puts response.read_body{
"token": "eyJraWQiOiJuNG93MStFaVdQeThpSVJLR2o1YXNvaURJTmNWNnJPaG9UWDNrXC9JN05Vaz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIzNDQ4OTQ0OC03MDgxLTcwYjItN2NkZC00MDEzZWU5YjkzMmQiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV80UEVLNUpncHgiLCJjbGllbnRfaWQiOiI0c2JoczN1dDMxaTE5b2lpY2lmNmlocGpuMSIsIm9yaWdpbl9qdGkiOiIyMzJjYmQ3My05YmEzLTQyM2MtYTU5YS1lYmJmMDM3MjJhZGIiLCJldmVudF9pZCI6IjA0YWI2MTEwLTA3ZGEtNDZmYy04N2MwLTY0ODAyYzgzNzZmMyIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE3Mzc0OTYxNjQsImV4cCI6MTczNzUwNjk2NCwiaWF0IjoxNzM3NDk2MTY0LCJqdGkiOiIxOTlhM2M0YS0yYTJkLTQxMjgtOWQ4Mi01YmQ3YjFjMGYwZTAiLCJ1c2VybmFtZSI6IjM0NDg5NDQ4LTcwODEtNzBiMi03Y2RkLTQwMTNlZTliOTMyZCJ9.gFI515igfGK9wNijA0fI-usEyaDatdJG5GZK0_byZoPWGQ9VoViaU1TZ_CtyI-rG6DN_-DJ3IBaUtrXbRZb8LIGRs8bvzKRdSyE3fRAsYht7NlczRRVH2w0TkgRX_8O8FZWERbOT2d3FfS_WVh5TG15QZGXmJBaTwTjKkxtQ5WZg_QGIocXfFA5j5vFOmKXEFsp_r7VzDA12BQBGaSq99li0aGLlROGY-svzskS_HUzlP3zRGuaaKv1vte0DUqC-4FMADhnSqw0pFvJcU6W5F3snuqRaHyKYD0g5Q3UBYm4fgBw-CHXz8jzB43yHU_I_X70OlOFWDjFwoONwX8-HXQ",
"expires": "2026-10-20 06:53:25"
}