Skip to main content
This page tracks new endpoints, breaking changes, methodology version updates, and coverage expansion. Within /v1, we only ever add fields, endpoints, and enum values. See Versioning.
Every listing we hold is reachable by its venue, the API describes itself, and a gold vehicle names its own methodology
  • Sixteen more exchanges have a prefix. Listings on Borsa Istanbul (IST), Tel Aviv (TLV), Ho Chi Minh City (HOSE), the Philippines (PSE), Boursa Kuwait (KWSE), Santiago (BCS), Athens (ATH), Egypt (EGX), Colombia (BVC), Nasdaq Iceland (ICE), Moscow (MCX), Lima (BVL), Casablanca (CAS), Prague (PRG), Nigeria (NGX) and Bucharest (BVB) read IST:THYAO instead of a bare ticker that a lookup resolves to a US listing first. The prefix follows the venue only, so a company from those countries listed on Nasdaq or NYSE keeps its bare ticker, and every identifier issued before still reaches the same listing. See Instrument identifiers.
  • The Evidence Packet names the XBRL concepts behind its values. sources[].xbrl_tags lists the concepts whose facts, for the period each value measures, add up to each value in the source’s raw_values exactly. Tags appear as results are analysed again: Apple’s debt screen will then read LongTermDebtNoncurrent, LongTermDebtCurrent and CommercialPaper. The list stays empty for a value we could not rebuild from the filings’ facts, for one that rests on a subtraction, for one taken from another source, and for results analysed before this release. See Evidence Packets.
  • Credicorp, SFL and Ardmore Shipping read as their NYSE tickers. instrument_id is BAP, SFL and ASC. They were stored as Danish companies and read CPH:BAP, CPH:SFL and CPH:ASC, which named a Copenhagen listing that does not exist; those ids now return 404.
  • GET /instruments/{symbol} reports the same instrument_id as the other endpoints. A listing addressed through its venue, such as ANTA (HKG:2020), read a bare ticker there; it and exchange now come from the listing itself.
  • A company with no revenue shows its interest as prohibited income. With revenue.total 0 and a prohibited share above zero, revenue.prohibited.amount and breakdown.interest_income carry the interest income the screen fails on; they read 0 before.
  • Every instrument response names its listing. /compliance, /financials, /history and /health add instrument_id beside symbol, as the overview and search already did: symbol alone can name two companies (AI is C3.ai in the US and Air Liquide in Paris). The MCP tools lead their summaries with it.
  • The prohibited-revenue breakdown sums to its amount. revenue.prohibited.breakdown adds other: prohibited revenue in none of the six named categories, such as a conventional bank’s underwriting, card and deposit fees. JPMorgan’s breakdown named 99.4billionofinterestagainst99.4 billion of interest against 138.3 billion prohibited. A named category other than interest is filled only when the analysis recorded it separately; otherwise that revenue is in other.
  • ETF purity dates its holdings. purity.holdings_as_of on GET /etfs/{symbol} is the date of the holdings snapshot the figure weighs. as_of stays the time of the calculation, which for a live figure is the request itself.
  • A stock ticker sent to /etfs names the right endpoint. The 404 says 'PAR:AI' is a stock, not a fund and points to /instruments/{symbol}/compliance, as /instruments already points a fund ticker to /etfs.
  • Rate-limit refusals use the error envelope. The per-IP limit on requests before key validation and the limit on GET /v1 and the OpenAPI document answered 429 with a different body. They now return error.code: rate_limit_exceeded with retry_after and meta.request_id, like every other error.
  • A pending determination says when we hold the verdict back. GET /instruments/{symbol}/compliance adds determination.withheld and GET /instruments/{symbol} adds compliance.withheld: the reason category, whether we analysed the instrument, and the date we recorded it. Qatar Islamic Bank (QSE:QIBK) was screened from its audited statements and reads screen_unresolved, not “not analysed”. The field is null everywhere else and status stays pending, so existing clients are unaffected. MCP screen_stock reports it. See When we analysed it and hold the verdict back.
  • A pooled vehicle names the methodology it was decided under. A gold or silver trust, a London-listed physical-metal ETC (LON:SGLN, LON:SSLN, LON:RMAP) or a futures pool is decided on the structure of the vehicle, not on ratios, and GET /instruments/{symbol}/compliance published every one of them as aaoifi-ss21@2026.1 with three passing ratio screens at 0%. Such a result now carries aaoifi-ss57@2026.1, aaoifi-ss20@2026.1 or halal-sh-etf-taxonomy@2026.1 in methodology and meta.methodology, reports the three ratio screens as not_applicable, and adds screens.structural: the criteria, each verdict and the quoted evidence behind it. The Evidence Packet carries one screen per criterion with the same quotes, /financials reports the ratios as not_applicable, and GET /methodologies lists the three structural methodologies with their criteria. MCP screen_stock renders the checklist and get_methodology takes the new ids. An operating company’s response is unchanged. See Pooled vehicles.
  • A prefix names the listing venue, whatever the company’s domicile. 556 of the 653 Hong Kong listings we cover are Cayman, PRC, Bermuda or BVI companies, and a strict country match made every one of them a 404 through HKG:. HKG:2020 (ANTA), HKG:1088 (China Shenhua) and HKG:1211 (BYD) now resolve, as do Jersey and Guernsey companies on the LSE through LON:, and Cayman companies on the TWSE through TPE:. A Hong Kong code also answers to any zero-padding (HKG:992, HKG:0992, HKG:00992 all reach Lenovo).
  • Alternative venue spellings resolve. Google Finance’s codes (EPA:AI, EBR:UCB, ETR:SAP, BIT:ENI), the venues’ own names (HKEX:0669, LSE:AZN, NZX:FPH, BME:AMS, XETRA:SAP) and a US venue written as a prefix (NASDAQ:AAPL) are all accepted. TSE: reads a numeric ticker as Tokyo and an alphabetic one as Toronto.
  • instrument_id on every instrument and search hit. The exact string to send to /instruments/{symbol}. symbol + country_code could not always be turned into a working request (a Cayman domicile has no prefix of its own); now the API says it outright.
  • POST /screen accepts exchange-prefixed identifiers. August’s entry below made it 400 on a prefix because the route’s existence check matched raw strings. The check now resolves through the same parser as /instruments, so a European or Hong Kong portfolio screens in one call, each row echoing the identifier in upper case.
  • The API describes itself. GET /v1 lists every endpoint and the docs; GET /v1/openapi.json (and .yaml) serves this site’s OpenAPI document, so an agent framework can load it by URL; neither needs a key. GET /v1/me reports the caller’s plan, key, limits and today’s usage split into direct and MCP traffic, so a lapsed plan is distinguishable from a bad key without opening the dashboard.
  • GET /instruments/{symbol}/health says why a non-US listing has none. Financial health is computed from SEC filings, so a listing that does not file with the SEC has no health analysis; the 404 now says so instead of reading like a wrong identifier. The compliance determination is unaffected.
  • Error codes match the reference. A 400 carries bad_request (it said validation_error). A spent sandbox Evidence Packet quota answers 403 sandbox_restricted, and a quota check that cannot reach its store answers 503 service_unavailable; both answered 403 forbidden. An unknown path answers in the /v1 error envelope with a request_id. A body that is not valid JSON answers 400 instead of 500, and /mcp answers it as a JSON-RPC parse error (-32700).
  • Rate limits follow your plan on every route. A per-IP limit meant for GET /v1 and the OpenAPI document ran on every /v1 route: it held Plus and Enterprise keys to 60 requests a minute and put every MCP user under one shared limit. It now covers those discovery routes only. A ceiling of 600 requests a minute per IP, above every plan’s own limit, stays in front of key validation.
  • POST /screen filter mode. status: "pending" selects unanalysed stocks (it returned every stock). A limit or offset that is not a whole number answers 400. Each row adds instrument_id and country_code: a bare BA can be Boeing or BAE Systems, and instrument_id is what to send to /instruments/{symbol}. A sandbox key’s symbol list accepts a venue spelling of a sandbox stock, such as NASDAQ:AAPL.
  • GET /etfs/LON:ISWD resolves, the form this reference and the MCP get_etf tool give. A non-US prefix never answers with a US-listed fund, or the reverse.
  • The Evidence Packet for a trust decided on its structure carries no ratio screens. GLD, IAU, IBIT and 26 other trusts analysed before the checklist was stored came back with three ratio screens at zero; they now carry the business-activity screen only, as /compliance does. meta.methodology names the result’s methodology on /compliance, /financials and /evidence, and xbrl_tags no longer lists the guessed tags it carried before. MCP get_evidence_packet returns a packet with no filing period instead of an output validation error, and /mcp answers an unknown or revoked key with 401 invalid_token when the client connects.
  • The Evidence Packet names its listing. instrument adds instrument_id (PAR:AI, what to send to /instruments/{symbol}) and fills exchange, and as_of is the date of the analysis, as on /compliance, not the day the packet was built.
  • Filter-mode POST /screen takes country and sort. country narrows to a country of domicile (GB), and sort: "market_cap" lists the largest first; before, every page was alphabetical across every market. A page past the end reports the real meta.total rather than 0.
  • MCP discover_stocks. Stocks that match a compliance status, sector, market-cap band, health rating or country, each with the instrument_id to pass to screen_stock. Plus plan.
Fund tickers route to the fund endpoint, and access follows your live plan
  • Fund tickers no longer come back as unanalysed stocks. Around 180 fund tickers also carry an equity row from crawler seeding, so POST /screen answered for SPY, QQQ and friends with a permanent pending, and GET /instruments/{symbol}/compliance did the same. A fund with no company determination is now reported as one: /screen returns it in meta.not_found and names it in the new meta.etfs, and the compliance endpoint adds meta.etf with the endpoint that does have the answer. Commodity and crypto trusts we screen under AAOIFI directly (GLD, IAU, SLV, IBIT, USO) still return their real determination. Additive: no field was removed or renamed.
  • GET /etfs/{symbol} accepts a fund’s alternate listing line. A same-ISIN currency or exchange ticker (ISDW for ISWD) resolved in the app but 404ed here. It now resolves to the fund, as /search always implied it would.
  • Access follows your active subscription, not the credential. A hsh_live_ key minted while Plus was active kept the full universe after the plan ended. Universe, rate limits and the Evidence Packet quota are now all derived from the plan in force at request time. If Plus lapses, existing live keys keep working at sandbox scope and sandbox limits, and renewing restores full access within five minutes with no new key. A signed-in session is scoped the same way, by the account’s plan.
  • MCP search_methodology answers with the relevant sub-section. It returned whole top-level knowledge-base sections, one of which is 43KB, so a single call could put roughly 50KB of mostly unrelated text into an agent’s context. It now returns the best-matching sub-sections within a size budget, each tagged with a dotted section path, and takes a new section argument to read any one of them in full.
  • MCP screen_portfolio passes the new etfs list through, so an agent re-routes a fund to get_etf instead of reporting it as an unknown ticker.
  • GET /instruments/{symbol}/evidence really does honour the exchange prefix now. July’s entry below said it did. Half of that change shipped: the cache key started including the country, but the route still called the builder without one, so every request resolved US-first and then cached that answer under the country-less key. A prefixed request (TYO:GS, GS Yuasa) returned Goldman Sachs’ packet, and so did the cache for the rest of its TTL. Now the country reaches the builder.
  • Non-US identifiers are documented, and a wrong one now corrects itself. A non-US listing is addressed with an exchange prefix (PAR:AI), not a vendor suffix (AI.PA), and nothing said so. Sending the vendor form now returns an error naming the prefixed form to use, and the format has a page of its own: Instrument identifiers.
  • POST /screen says what it can resolve. It screens bare tickers only, and an exchange-prefixed identifier used to land silently in meta.not_found, which reads as “we don’t cover that company”. It now returns 400 naming GET /instruments/{symbol} as the route that resolves it. The MCP tools’ symbol arguments were widened to 20 characters so a prefixed identifier reaches the instrument endpoints intact.
Cross-listing resolution and smarter search matching
  • Cross-listings and secondary share classes now resolve to their company’s data. Compliance results, health analyses and filings are stored once against a company’s canonical listing. Requesting a different listing of the same company (an ADR, a foreign line, or a second share class) previously read that listing’s own (empty) rows, so GET /instruments/{symbol}/health and /history could return 404 for a company we had fully analysed. Both now resolve to the canonical company and return the same answer for every listing of it. Responses still echo the symbol and name you asked for. Fixes-only: no field or shape changed.
  • GET /instruments/{symbol}/evidence now honours the exchange prefix. It previously ignored the country part of the identifier, so a prefixed symbol that collides with a US ticker (TYO:GS is GS Yuasa, not Goldman Sachs) could return the wrong company’s packet. Cached packets are keyed per listing.
  • GET /search matches multi-word queries word-by-word. A query whose words are separated in the target name now matches: Technology fund finds Vanguard Information Technology Index Fund, where it previously returned nothing. Whole-phrase matches still rank above scattered-word matches, so existing single-word queries return the same results in the same order.
  • % and _ in a search query are now literal characters, not SQL wildcards. a%e returns funds and companies actually containing “a%e” rather than three unrelated tickers.
Search now returns ETFs
  • GET /search now returns ETFs alongside stocks. Every result carries a type discriminator (stock | etf). Stock items are unchanged; ETF items expose Shariah purity (purity_percentage, badge_color) and match_coverage_percent instead of a compliance status. This is additive: existing stock-only integrations keep working and can ignore ETF rows by type.
  • New types filter. Pass types=stock (or types=etf) to narrow to one kind; omit it for both. Any value outside the {stock, etf} allow-list returns 400.
  • The MCP search_instruments tool returns the mixed results automatically and tags each hit with its type.
Personal API, MCP, and filter screening included with Plus
  • Personal API, MCP, and universe-filter screening are now included with Plus. Live keys, full MCP access, and filter-mode screening all come with the single paid tier.
  • The previously-planned consumer Pro tier is discontinued. (Building a product on halal.sh remains the separate developer plan.)
Key dashboard moved to halal.sh/account/api
  • Key management has a new home. Create and revoke keys at halal.sh/account/api; halal.sh/developers is now the commercial overview page. Live keys come with the halal.sh Plus plan.
  • The MCP setup guide moved to /mcp-server, because docs.halal.sh/mcp is the hosted docs-MCP endpoint itself.
  • Setup guides for ChatGPT, Claude, Gemini CLI, Cursor, and VS Code on the MCP server page. The ChatGPT and Claude apps connect by signing in with a halal.sh account; no key handling needed.
v1 surface finalized
The /v1 API surface is finalized and stable:
  • Instruments: overview, compliance, financials, Evidence Packet, compliance history, and financial health.
  • Screening: batch screening by symbol, and search.
  • ETFs: purity, paginated holdings with per-holding compliance, and purity history.
  • Methodology: list and detail for aaoifi-ss21@2026.1.
Sandbox keys are live for the curated universe. This changelog will record all changes once live keys are generally available.
What to expect going forward
  • New endpoint announcements
  • Methodology version bumps (e.g. aaoifi-ss21@2026.1 → 2026.2)
  • Breaking-change notices with migration guides (new major versions only)
  • Coverage-expansion updates