curl --request POST \
--url https://api.halal.sh/v1/analysis-requests \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"symbol": "TYO:7203",
"notify": true
}
'import requests
url = "https://api.halal.sh/v1/analysis-requests"
payload = {
"symbol": "TYO:7203",
"notify": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({symbol: 'TYO:7203', notify: true})
};
fetch('https://api.halal.sh/v1/analysis-requests', 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://api.halal.sh/v1/analysis-requests",
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([
'symbol' => 'TYO:7203',
'notify' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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://api.halal.sh/v1/analysis-requests"
payload := strings.NewReader("{\n \"symbol\": \"TYO:7203\",\n \"notify\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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://api.halal.sh/v1/analysis-requests")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"symbol\": \"TYO:7203\",\n \"notify\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.halal.sh/v1/analysis-requests")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"symbol\": \"TYO:7203\",\n \"notify\": true\n}"
response = http.request(request)
puts response.read_body{
"data": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"instrument_id": "TYO:7203",
"status": "queued",
"lane": "priority",
"requested_at": "2023-11-07T05:31:56Z",
"promised_by": "2023-11-07T05:31:56Z",
"scheduled_for": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"progress_percentage": 50,
"determination": "compliant",
"notify": true,
"links": {
"self": "/v1/analysis-requests/6f1c2d4e-0000-4000-8000-000000000001",
"compliance": "/v1/instruments/TYO:7203/compliance"
},
"joined_existing": true
},
"meta": {
"request_id": "req_abc123",
"as_of": "2023-11-07T05:31:56Z",
"methodology": "aaoifi-ss21@2026.1",
"estimated_minutes": 123
}
}{
"data": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"instrument_id": "TYO:7203",
"status": "queued",
"lane": "priority",
"requested_at": "2023-11-07T05:31:56Z",
"promised_by": "2023-11-07T05:31:56Z",
"scheduled_for": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"progress_percentage": 50,
"determination": "compliant",
"notify": true,
"links": {
"self": "/v1/analysis-requests/6f1c2d4e-0000-4000-8000-000000000001",
"compliance": "/v1/instruments/TYO:7203/compliance"
},
"joined_existing": true
},
"meta": {
"request_id": "req_abc123",
"as_of": "2023-11-07T05:31:56Z",
"methodology": "aaoifi-ss21@2026.1",
"estimated_minutes": 123
}
}{
"error": {
"code": "bad_request",
"message": "Maximum 50 symbols per request."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "unauthorized",
"message": "Invalid or revoked API key."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "plan_required",
"message": "Requesting an analysis needs a paid plan. The Plus plan lets you request an analysis of any stock we have not screened yet, for $7 a month: https://halal.sh/plans?source=api_refusal. To show results in your own product, you need the Developer plan, at $99 a month: https://halal.sh/developers.",
"upgrade": {
"plan": "plus",
"price_usd_monthly": 7,
"url": "https://halal.sh/plans?source=api_refusal",
"commercial_plan": "developer",
"commercial_price_usd_monthly": 99,
"commercial_url": "https://halal.sh/developers"
}
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "not_found",
"message": "Stock with identifier 'XYZ' not found"
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "already_analyzed",
"message": "This stock already has compliance analysis."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "no_filings",
"message": "We can't analyze this stock because we hold no filings we can screen it from."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "analysis_limit_reached",
"message": "You already have 3 analyses in progress. Please wait for one to finish before requesting another.",
"retry_after": 2100,
"limit_type": "in_progress",
"limit": 3,
"current": 3,
"reset_at": "2026-11-02T13:35:00.000Z"
},
"meta": {
"request_id": "req_abc123"
}
}Request an Analysis
Ask us to analyze a stock that has no compliance result yet. This is the request the “Request analysis” button on a stock page makes, and it needs the Plus or Developer plan.
We run one analysis per stock however many people ask. If one is
already running, you join it: the reply is 200 with
joined_existing: true and that run’s request_id. A new request
gets 202. Either way, poll links.self until status is
complete, then read the result at links.compliance. We don’t
email you about the request unless you send "notify": true; with it,
we email the account when we publish the result, or if the analysis
goes to our compliance team for review.
Each new request counts against your account’s on-demand analysis
allowance, the same one the website uses: 10 a day on the Plus and
Developer plans, with up to 3 running at once. Joining a run already
in progress doesn’t count. Past the allowance the reply is 429 analysis_limit_reached, and retry_after is the number of seconds
until you can ask again. Send an account’s requests one at a time: we
accept one every 2 seconds per account, and a faster one gets 429 rate_limit_exceeded.
When we can’t analyze the stock, we refuse it with one of these codes:
409 already_analyzed: the stock already has a result. Read it from/instruments/{symbol}/compliance. A secondary share class whose primary we’ve analyzed gets this too, with the primary inerror.instrument_id.422 no_filings: we hold no filings we can screen the company from.422 stale_filings: its latest annual report is too old for a reliable first analysis.422 withheld: we’ve reviewed the company and can’t publish a verdict yet; the reason is inmessage.409 listing_not_active: the listing is delisted or inactive; its status and delisting date are inerror.statusanderror.delisted_at.
We accept funds, trusts and other pooled vehicles, and our compliance
team reviews them, so they come back as under_review.
curl --request POST \
--url https://api.halal.sh/v1/analysis-requests \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"symbol": "TYO:7203",
"notify": true
}
'import requests
url = "https://api.halal.sh/v1/analysis-requests"
payload = {
"symbol": "TYO:7203",
"notify": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({symbol: 'TYO:7203', notify: true})
};
fetch('https://api.halal.sh/v1/analysis-requests', 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://api.halal.sh/v1/analysis-requests",
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([
'symbol' => 'TYO:7203',
'notify' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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://api.halal.sh/v1/analysis-requests"
payload := strings.NewReader("{\n \"symbol\": \"TYO:7203\",\n \"notify\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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://api.halal.sh/v1/analysis-requests")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"symbol\": \"TYO:7203\",\n \"notify\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.halal.sh/v1/analysis-requests")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"symbol\": \"TYO:7203\",\n \"notify\": true\n}"
response = http.request(request)
puts response.read_body{
"data": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"instrument_id": "TYO:7203",
"status": "queued",
"lane": "priority",
"requested_at": "2023-11-07T05:31:56Z",
"promised_by": "2023-11-07T05:31:56Z",
"scheduled_for": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"progress_percentage": 50,
"determination": "compliant",
"notify": true,
"links": {
"self": "/v1/analysis-requests/6f1c2d4e-0000-4000-8000-000000000001",
"compliance": "/v1/instruments/TYO:7203/compliance"
},
"joined_existing": true
},
"meta": {
"request_id": "req_abc123",
"as_of": "2023-11-07T05:31:56Z",
"methodology": "aaoifi-ss21@2026.1",
"estimated_minutes": 123
}
}{
"data": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"instrument_id": "TYO:7203",
"status": "queued",
"lane": "priority",
"requested_at": "2023-11-07T05:31:56Z",
"promised_by": "2023-11-07T05:31:56Z",
"scheduled_for": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"progress_percentage": 50,
"determination": "compliant",
"notify": true,
"links": {
"self": "/v1/analysis-requests/6f1c2d4e-0000-4000-8000-000000000001",
"compliance": "/v1/instruments/TYO:7203/compliance"
},
"joined_existing": true
},
"meta": {
"request_id": "req_abc123",
"as_of": "2023-11-07T05:31:56Z",
"methodology": "aaoifi-ss21@2026.1",
"estimated_minutes": 123
}
}{
"error": {
"code": "bad_request",
"message": "Maximum 50 symbols per request."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "unauthorized",
"message": "Invalid or revoked API key."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "plan_required",
"message": "Requesting an analysis needs a paid plan. The Plus plan lets you request an analysis of any stock we have not screened yet, for $7 a month: https://halal.sh/plans?source=api_refusal. To show results in your own product, you need the Developer plan, at $99 a month: https://halal.sh/developers.",
"upgrade": {
"plan": "plus",
"price_usd_monthly": 7,
"url": "https://halal.sh/plans?source=api_refusal",
"commercial_plan": "developer",
"commercial_price_usd_monthly": 99,
"commercial_url": "https://halal.sh/developers"
}
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "not_found",
"message": "Stock with identifier 'XYZ' not found"
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "already_analyzed",
"message": "This stock already has compliance analysis."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "no_filings",
"message": "We can't analyze this stock because we hold no filings we can screen it from."
},
"meta": {
"request_id": "req_abc123"
}
}{
"error": {
"code": "analysis_limit_reached",
"message": "You already have 3 analyses in progress. Please wait for one to finish before requesting another.",
"retry_after": 2100,
"limit_type": "in_progress",
"limit": 3,
"current": 3,
"reset_at": "2026-11-02T13:35:00.000Z"
},
"meta": {
"request_id": "req_abc123"
}
}Body
Email the account when we publish the result, or if the analysis goes to our compliance team for review. Off by default, because a program polling for results doesn't need an email per stock. When you join an analysis someone else started, it covers only the email when we publish the result, and it applies to you alone. While a request you've made is still running, sending it again with true turns this on for it.

