{"components":{"parameters":{"CallPeriod":{"description":"Only calls whose return over this window has been realised — `3m` means \"calls old enough that their 3-month return is known\", not \"calls from the last three months\". Omit it to include every call.\n","in":"query","name":"period","schema":{"enum":["1m","3m","1y"],"type":"string"}},"Limit":{"description":"Between 1 and 100; defaults to 20. Larger values are rejected, not clamped.","in":"query","name":"limit","schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},"Page":{"description":"1-based page number.","in":"query","name":"page","schema":{"default":1,"minimum":1,"type":"integer"}},"StockCode":{"description":"The stock's exchange code, e.g. `MAYBANK`. Case-sensitive.","in":"path","name":"code","required":true,"schema":{"type":"string"}}},"responses":{"InternalError":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"An unexpected server-side failure. Retry with backoff."},"InvalidRequest":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"A parameter is missing, malformed, over a cap, or combined with one it excludes."},"NotFound":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"No resource with that identifier."},"PaymentRequired":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"The key's account has no active Ultra subscription."},"RateLimited":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Over the plan quota, or over the search limit on `/news`.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}}},"Unauthorized":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"The API key is missing, malformed, revoked or expired."}},"schemas":{"AnalystDetail":{"allOf":[{"$ref":"#/components/schemas/AnalystSummary"},{"properties":{"distribution":{"$ref":"#/components/schemas/CallDistribution"}},"required":["distribution"],"type":"object"}]},"AnalystPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AnalystSummary"},"type":"array"},"page":{"$ref":"#/components/schemas/PageInfo"}},"required":["data","page"],"type":"object"},"AnalystSummary":{"properties":{"active":{"type":"boolean"},"broker_name":{"type":"string"},"id":{"description":"Opaque; obtained from `/analysts`.","format":"uuid","type":"string"},"name":{"type":"string"},"sector":{"type":"string"},"total_forecasts":{"type":"integer"},"track_record":{"description":"One entry per realised-return window.","items":{"$ref":"#/components/schemas/AnalystTrackRecord"},"type":"array"}},"required":["id","name","broker_name","sector","active","total_forecasts","track_record"],"type":"object"},"AnalystTrackRecord":{"properties":{"average_return":{"type":["number","null"]},"period":{"enum":["1m","3m","12m"],"type":"string"},"rating_score":{"type":["number","null"]},"success_rate":{"type":["number","null"]},"total_calls":{"type":"integer"}},"required":["period","rating_score","average_return","success_rate","total_calls"],"type":"object"},"Call":{"properties":{"ai_summary":{"description":"Present on premium and elite keys only.","type":"string"},"analyst_id":{"format":"uuid","type":"string"},"analyst_name":{"type":"string"},"broker_name":{"type":"string"},"call_price":{"type":["number","null"]},"call_type":{"enum":["Buy","Sell","Hold","Cease","NotRated"],"type":"string"},"call_type_change":{"enum":["Up","Down","Unchanged",null],"type":["string","null"]},"date":{"format":"date","type":"string"},"dividend_per_share":{"type":["number","null"]},"earning_per_share":{"type":["number","null"]},"id":{"format":"uuid","type":"string"},"returns_12m":{"type":["number","null"]},"returns_1m":{"description":"Realised return over the window; null until it is known.","type":["number","null"]},"returns_3m":{"type":["number","null"]},"stock_code":{"type":"string"},"stock_name":{"type":"string"},"target_price":{"type":["number","null"]},"target_price_change":{"enum":["Up","Down","Unchanged",null],"type":["string","null"]},"upside_potential":{"type":["number","null"]}},"required":["id","date","stock_code","stock_name","analyst_id","analyst_name","broker_name","call_type","call_type_change","target_price","target_price_change","call_price","upside_potential","earning_per_share","dividend_per_share","returns_1m","returns_3m","returns_12m"],"type":"object"},"CallDistribution":{"properties":{"call_types":{"items":{"$ref":"#/components/schemas/CallTypeCount"},"type":"array"},"period":{"type":"string"}},"required":["period","call_types"],"type":"object"},"CallPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Call"},"type":"array"},"page":{"$ref":"#/components/schemas/PageInfo"}},"required":["data","page"],"type":"object"},"CallTypeCount":{"properties":{"call_type":{"enum":["Buy","Sell","Hold","Cease","NotRated"],"type":"string"},"count":{"type":"integer"}},"required":["call_type","count"],"type":"object"},"Error":{"properties":{"code":{"enum":["invalid_key","subscription_required","rate_limited","not_found","invalid_request","internal_error"],"type":"string"},"hint":{"description":"What to change or do next; absent when there is nothing actionable.","type":"string"},"message":{"type":"string"}},"required":["code","message"],"type":"object"},"ErrorEnvelope":{"properties":{"error":{"$ref":"#/components/schemas/Error"}},"required":["error"],"type":"object"},"Me":{"properties":{"key_id":{"format":"uuid","type":"string"},"key_prefix":{"description":"The first characters of the key, for telling keys apart.","type":"string"},"rate_limit":{"properties":{"per_minute":{"type":"integer"}},"required":["per_minute"],"type":"object"},"tier":{"enum":["basic","premium","elite"],"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","tier","key_id","key_prefix","rate_limit"],"type":"object"},"NewsResult":{"properties":{"analysts":{"items":{"properties":{"broker":{"type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"}},"required":["id","name","broker"],"type":"object"},"type":"array"},"author":{"type":"string"},"excerpt":{"description":"A teaser recomputed at read time; never carries elite body text.","type":"string"},"feature_image_url":{"type":"string"},"is_elite":{"type":"boolean"},"published_at":{"format":"date-time","type":["string","null"]},"slug":{"type":"string"},"stocks":{"items":{"properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code","name"],"type":"object"},"type":"array"},"summary":{"type":"string"},"title":{"type":"string"}},"required":["title","slug","author","summary","excerpt","feature_image_url","is_elite","published_at","stocks","analysts"],"type":"object"},"PageInfo":{"properties":{"has_next":{"type":"boolean"},"number":{"type":"integer"},"size":{"type":"integer"},"total_pages":{"type":"integer"},"total_results":{"type":"integer"}},"required":["size","number","total_results","total_pages","has_next"],"type":"object"},"RankingCall":{"properties":{"analyst_name":{"type":"string"},"broker_name":{"type":"string"},"date":{"format":"date","type":"string"},"type":{"enum":["Buy","Sell","Hold","Cease","NotRated"],"type":"string"}},"required":["type","analyst_name","broker_name","date"],"type":"object"},"RankingItem":{"properties":{"call":{"description":"Populated for call-based ranking types; null for stock-based ones.","oneOf":[{"$ref":"#/components/schemas/RankingCall"},{"type":"null"}]},"current_price":{"type":"number"},"rank":{"type":"integer"},"stock_code":{"type":"string"},"stock_name":{"type":"string"},"str_rating":{"type":["number","null"]},"target_price":{"type":["number","null"]},"upside_potential":{"type":["number","null"]}},"required":["rank","stock_code","stock_name","current_price","target_price","upside_potential","str_rating","call"],"type":"object"},"Sector":{"properties":{"ai_summary":{"type":"string"},"name":{"type":"string"}},"required":["name","ai_summary"],"type":"object"},"StockConsensus":{"description":"Computed on a fixed window; does not move with `period`.","properties":{"dividend_per_share":{"type":"number"},"earning_per_share":{"type":"number"},"highest_target":{"type":"number"},"lowest_target":{"type":"number"},"target":{"type":"number"},"total_buy_calls":{"type":"integer"},"total_ratings":{"type":"integer"},"upside_potential":{"type":"number"}},"required":["target","highest_target","lowest_target","upside_potential","total_ratings","total_buy_calls","earning_per_share","dividend_per_share"],"type":"object"},"StockDetail":{"allOf":[{"$ref":"#/components/schemas/StockSummary"},{"properties":{"consensus":{"$ref":"#/components/schemas/StockConsensus"},"distribution":{"$ref":"#/components/schemas/CallDistribution"},"overview":{"type":"string"},"recent_calls":{"description":"The five most recent calls on the stock.","items":{"$ref":"#/components/schemas/Call"},"type":"array"}},"required":["overview","consensus","distribution","recent_calls"],"type":"object"}]},"StockPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/StockSummary"},"type":"array"},"page":{"$ref":"#/components/schemas/PageInfo"}},"required":["data","page"],"type":"object"},"StockSummary":{"properties":{"change_percent":{"type":"number"},"code":{"description":"The stock's identifier throughout this API.","type":"string"},"consensus_target":{"type":"number"},"current_price":{"type":"number"},"dividend_yield":{"type":"number"},"market_cap":{"type":"integer"},"name":{"type":"string"},"price_change":{"type":"number"},"price_to_earning":{"type":"number"},"sector":{"type":"string"},"sentiment":{"description":"One of `Extremely Bullish`, `Mild Bullish`, `Neutral`, `Mild Bearish`, `Extremely Bearish`, `On Radar`, `Off Radar`, or empty when unassessed.\n","type":"string"},"sentiment_index":{"type":["number","null"]},"str_rating":{"description":"The STR composite rating; null when the stock is unrated.","type":["number","null"]},"sub_sector":{"type":"string"},"symbol":{"type":"string"},"total_ratings":{"type":"integer"},"trade_volume":{"type":"integer"},"updated_at":{"format":"date-time","type":"string"},"upside_potential":{"type":"number"}},"required":["code","name","symbol","sector","sub_sector","current_price","price_change","change_percent","market_cap","trade_volume","price_to_earning","dividend_yield","consensus_target","upside_potential","total_ratings","str_rating","sentiment_index","sentiment","updated_at"],"type":"object"}},"securitySchemes":{"apiKey":{"description":"An API key from your account page, of the form `str_live_…`. Requires an active Ultra subscription.\n","scheme":"bearer","type":"http"}}},"info":{"description":"Read-only access to StockTipRatings research: stocks, analyst calls,\nanalyst track records, semantic news search, rankings and sectors.\n\n**Authentication.** Every endpoint except this document requires an API key\nwith an active Ultra subscription, sent as `Authorization: Bearer str_live_…`.\nKeys are created on your account page. A missing or revoked key is `401\ninvalid_key`; a key whose account is inactive or on a lower plan is `402\nsubscription_required`.\n\n**Rate limiting.** Every response carries `X-RateLimit-Limit`,\n`X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix seconds) describing\nyour plan quota; a `429 rate_limited` adds `Retry-After` in seconds.\n`GET /news` is additionally metered per key by a separate search limit that\ndoes not appear in those headers.\n\n**Errors.** Every error is `{\"error\": {\"code\", \"message\", \"hint\"}}`. Invalid\nparameters are rejected with `400 invalid_request` rather than silently\nadjusted: an over-cap `limit`, an unknown `sort`, or a filter combined with\n`q` is an error, never a clamped or partially-applied query.\n\n**Pagination.** List endpoints take `page` (1-based) and `limit` and answer\n`{\"data\": […], \"page\": {\"size\", \"number\", \"total_results\", \"total_pages\",\n\"has_next\"}}`.\n","title":"StockTipRatings Public API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/analysts":{"get":{"description":"Without `q`, a paginated listing filterable by `broker` and `sector` and sortable by track-record fields. With `q`, a keyword search over analyst names; `q` cannot be combined with `broker`, `sector`, `sort` or `order`.\n","operationId":"listAnalysts","parameters":[{"description":"Keyword search. Mutually exclusive with `broker`, `sector`, `sort` and `order`.","in":"query","name":"q","schema":{"type":"string"}},{"description":"Exact broker name.","in":"query","name":"broker","schema":{"type":"string"}},{"description":"Exact sector name.","in":"query","name":"sector","schema":{"type":"string"}},{"description":"Defaults to `name`.","in":"query","name":"sort","schema":{"enum":["name","broker","rating_score_1m","rating_score_3m","rating_score_12m","average_return_1m","average_return_3m","average_return_12m","success_rate_1m","success_rate_3m","success_rate_12m"],"type":"string"}},{"description":"Defaults to `asc`.","in":"query","name":"order","schema":{"enum":["asc","desc"],"type":"string"}},{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalystPage"}}},"description":"One page of analysts."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"List or search analysts"}},"/analysts/{id}":{"get":{"description":"The analyst's profile, track record and call-type distribution. `id` is an opaque identifier obtained from `/analysts`; it is not guessable from the analyst's name.\n","operationId":"getAnalyst","parameters":[{"description":"An analyst id obtained from `/analysts`.","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Window for `distribution` only. Defaults to `1y`.","in":"query","name":"period","schema":{"enum":["3m","6m","1y","2y"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalystDetail"}}},"description":"The analyst."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"One analyst in full"}},"/calls":{"get":{"description":"Analyst calls across the whole market, most recent first, filterable by analyst, broker, date and rating status.\n","operationId":"listCalls","parameters":[{"description":"An analyst id obtained from `/analysts`.","in":"query","name":"analyst_id","schema":{"format":"uuid","type":"string"}},{"description":"Exact broker name, as returned in `broker_name` fields. An unknown name is `invalid_request`, not an empty result.\n","in":"query","name":"broker","schema":{"type":"string"}},{"description":"Calls made on exactly this day.","in":"query","name":"date","schema":{"format":"date","type":"string"}},{"$ref":"#/components/parameters/CallPeriod"},{"description":"When `true`, only calls that carry a rating (excludes `Cease` and `NotRated`).","in":"query","name":"rated","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallPage"}}},"description":"One page of calls."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"List calls across all stocks"}},"/me":{"get":{"description":"Identifies the key, its owner and its subscription tier, and reports the per-minute request quota the tier grants. `tier` also determines whether tier-gated fields such as `ai_summary` on calls are present.\n","operationId":"getMe","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}},"description":"The calling key."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"The calling key and its quota"}},"/news":{"get":{"description":"Vector search over StockTipRatings news and research commentary. Results are ordered by relevance; there is no relevance score field, and result order is the only relevance signal. Metered by a separate per-key search limit on top of the plan quota; that limit is not reflected in the `X-RateLimit-*` headers.\n","operationId":"searchNews","parameters":[{"description":"The search query.","in":"query","name":"q","required":true,"schema":{"type":"string"}},{"description":"Restrict to news mentioning these stocks. Comma-separated codes, at most 10; the parameter may also be repeated. An unknown code is `invalid_request` naming the code.\n","in":"query","name":"stock","schema":{"type":"string"}},{"description":"Restrict to news mentioning these analysts. Comma-separated ids obtained from `/analysts`; the parameter may also be repeated.\n","in":"query","name":"analyst_id","schema":{"type":"string"}},{"description":"Earliest publication day, inclusive.","in":"query","name":"published_from","schema":{"format":"date","type":"string"}},{"description":"Latest publication day, inclusive. Must not be before `published_from`.\n","in":"query","name":"published_to","schema":{"format":"date","type":"string"}},{"description":"Between 1 and 50; defaults to 10. Larger values are rejected, not clamped.","in":"query","name":"limit","schema":{"default":10,"maximum":50,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"items":{"$ref":"#/components/schemas/NewsResult"},"type":"array"}},"required":["data"],"type":"object"}}},"description":"Search results, most relevant first."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"Semantic search over research news"}},"/rankings":{"get":{"description":"One endpoint, seven ranking types. Every item carries the same top-level keys; `call` is populated for the call-based types (`top_calls`, `top_recommendations`, `top_forecasts`, `top_forecast_recommendations`) and `null` for the stock-based ones (`top_upside`, `bullish_picks`, `top_stocks`), so whether call detail is present follows from `type` alone. Some types are curated to a small fixed size and may return fewer items than `limit`.\n","operationId":"listRankings","parameters":[{"in":"query","name":"type","required":true,"schema":{"enum":["top_calls","top_recommendations","top_forecasts","top_forecast_recommendations","top_upside","bullish_picks","top_stocks"],"type":"string"}},{"description":"Between 1 and 100; defaults to 20. Larger values are rejected, not clamped.","in":"query","name":"limit","schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"Exact sector name. Applies to `type=top_stocks` only; on any other type it is `invalid_request` rather than silently ignored.\n","in":"query","name":"sector","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"items":{"$ref":"#/components/schemas/RankingItem"},"type":"array"},"type":{"type":"string"}},"required":["type","data"],"type":"object"}}},"description":"The ranking, best first."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"Ranked lists of calls and stocks"}},"/sectors/{name}":{"get":{"operationId":"getSector","parameters":[{"description":"Exact sector name, as returned in `sector` fields.","in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sector"}}},"description":"The sector."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"One sector"}},"/stocks":{"get":{"description":"Without `q`, a paginated listing filterable by `sector` and sortable by the whitelisted fields. With `q`, a keyword search over stock names and codes; `q` cannot be combined with `sector`, `sort` or `order`, because the search runs a different query and would drop them.\n","operationId":"listStocks","parameters":[{"description":"Keyword search. Mutually exclusive with `sector`, `sort` and `order`.","in":"query","name":"q","schema":{"type":"string"}},{"description":"Exact sector name, as returned in `sector` fields.","in":"query","name":"sector","schema":{"type":"string"}},{"description":"Defaults to `market_cap`.","in":"query","name":"sort","schema":{"enum":["market_cap","current_price","upside_potential","total_ratings","dividend_yield","price_to_earning","sentiment_index"],"type":"string"}},{"description":"Defaults to `desc`.","in":"query","name":"order","schema":{"enum":["asc","desc"],"type":"string"}},{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockPage"}}},"description":"One page of stocks."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"List or search stocks"}},"/stocks/{code}":{"get":{"description":"The stock's profile, consensus block, call-type distribution and five most recent calls, in one response. The consensus block is computed on a fixed window and does not move with `period`; only `distribution` reflects it.\n","operationId":"getStock","parameters":[{"$ref":"#/components/parameters/StockCode"},{"description":"Window for `distribution` only. Defaults to `1y`.\n","in":"query","name":"period","schema":{"enum":["3m","6m","1y","2y"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockDetail"}}},"description":"The stock."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"One stock in full"}},"/stocks/{code}/calls":{"get":{"description":"Analyst calls on the stock, most recent first.\n","operationId":"listStockCalls","parameters":[{"$ref":"#/components/parameters/StockCode"},{"$ref":"#/components/parameters/CallPeriod"},{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallPage"}}},"description":"One page of calls."},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"summary":"Calls on one stock"}}},"security":[{"apiKey":[]}],"servers":[{"url":"https://stocktipratings.com/api/v1"}]}