openapi: 3.0.3 info: title: Account Account API Ranking History API API version: v2 servers: - url: https://api.spyfu.com/apis/accounts_api security: - Basic_Authentication_Token: [] - Query_Parameter_Token: [] - HMAC_Authentication_Header: [] tags: - name: Ranking History API paths: /v2/historic/getHistoricRankingsForDomain: get: operationId: HistoricRanking_GetHistoricRankingsForDomain_GET summary: Get Domain Ranking History description: 'Returns historical organic ranking data for a domain across multiple keywords within a specified date range. Track position changes, click gains/losses, and ranking trends over time to analyze SEO performance. [Visualize this API live on SpyFu](https://www.spyfu.com/seo/ranking-history/domain?expandChart=false&searchType=MostValuable&includeAnyTerm=true&query=example.com&sidebarContext=topics) _(TODO – verify)_' parameters: - name: domain in: query description: Domain to analyze ranking history for. required: true schema: type: string example: example.com example: example.com - name: queryType in: query description: Type of ranking analysis to perform. schema: type: string enum: - MostValuable - FellFromTop10 - MadeTheTop10 - NewKeywords - NoLongerRanks - GainedRanks - LostRanks - GainedClicks - LostClicks example: MostValuable example: MostValuable - name: startDate in: query description: Beginning date for range schema: type: string format: date-time example: 2025-01 example: 2025-01 - name: endDate in: query description: Ending date for range schema: type: string format: date-time example: 2025-07 example: 2025-07 - name: includeTerms in: query description: Comma-separated list of terms that must be present in the keyword. schema: type: string example: hosting,domain,website - name: includeAnyTerm in: query description: 'Used with includeTerms. If true: match any term (OR). If false: require all terms (AND).' schema: type: boolean default: false example: false - name: excludeTerms in: query description: Comma-separated list of terms to exclude from results (e.g., branded or irrelevant terms). schema: type: string example: free,cheap,discount - name: startRank.min in: query description: Filter to keywords where the domain/URL starting rank position is ≥ this value for the selected date range (1 = best/top organic result). schema: type: number format: float - name: startRank.max in: query description: Filter to keywords where the domain/URL starting rank position is ≤ this value for the selected date range (1 = best/top organic result). schema: type: number format: float - name: endRank.min in: query description: Filter to keywords where the domain/URL ending rank position is ≥ this value for the selected date range (1 = best/top organic result). schema: type: number format: float - name: endRank.max in: query description: Filter to keywords where the domain/URL ending rank position is ≤ this value for the selected date range (1 = best/top organic result). schema: type: number format: float - name: endRankChange.min in: query description: Filter to keywords where the rank improved by at least this many positions between the start and end of the date range (end_rank_change ≥ value). Positive values mean moved up; negative values mean moved down. schema: type: number format: float - name: endRankChange.max in: query description: Filter to keywords where the rank change was at most this many positions between the start and end of the date range (end_rank_change ≤ value). Positive values mean moved up; negative values mean moved down. schema: type: number format: float - name: seoClicks.min in: query description: Filter to keywords where estimated monthly organic clicks (SEO clicks) to the domain/url are ≥ this value. schema: type: number format: float - name: seoClicks.max in: query description: Filter to keywords where estimated monthly organic clicks (SEO clicks) to the domain/url are ≤ this value. schema: type: number format: float - name: seoClicksChange.min in: query description: Filter to keywords where the month-over-month change in estimated organic clicks is ≥ this value. Positive values indicate click gains; negative values indicate click losses. schema: type: number format: float - name: seoClicksChange.max in: query description: Filter to keywords where the month-over-month change in estimated organic clicks is ≤ this value. Positive values indicate click gains; negative values indicate click losses. schema: type: number format: float - name: searchVolume.min in: query description: Filter to keywords where monthly search volume (Google) is ≥ this value. schema: type: number format: float - name: searchVolume.max in: query description: Filter to keywords where monthly search volume (Google) is ≤ this value. schema: type: number format: float - name: countryCode in: query description: Country market to search. Specifically, this maps to the Google domain version to query against (e.g., google.com for US, google.de for Germany, etc.). All Countries schema: type: string default: US enum: - AR - AT - AU - BE - BR - CA - CH - DE - DK - ES - FR - IE - IN - IT - JP - MX - NL - 'NO' - NZ - PL - PT - SE - SG - TR - UA - UK - US - ZA example: US example: US - name: sortBy in: query description: Field to sort by schema: type: string enum: - ClicksChange - EndClicks - StartRank - EndRank - RankChange - name: pageSize in: query description: The maximum number of rows returned. schema: type: integer format: int32 default: 5 maximum: 10000 minimum: 1 example: 5 example: 5 - name: startingRow in: query description: Row number to start the results with. schema: type: integer format: int32 default: 1 maximum: 10000 minimum: 1 example: 1 example: 1 - name: sortOrder in: query description: Order to sort by schema: type: string enum: - Ascending - Descending responses: '200': description: Successfully retrieved domain ranking history data content: application/json: schema: type: object properties: resultCount: description: Number of results returned type: integer format: int32 readOnly: true example: 100 results: type: array items: type: object properties: keyword: type: string nullable: true historicalRanks: type: object additionalProperties: type: integer format: int32 nullable: true startRank: type: integer format: int32 nullable: true endRank: type: integer format: int32 nullable: true rankChange: type: integer format: int32 nullable: true endClicks: type: integer format: int64 clicksChange: type: integer format: int64 searchVolume: type: integer format: int64 nullable: true additionalProperties: false readOnly: true nullable: true totalVolume: type: integer format: int64 readOnly: true totalClicks: type: integer format: int64 readOnly: true totalClicksChange: type: integer format: int64 readOnly: true totalRankChange: type: integer format: int64 readOnly: true rankAverage: type: number format: double readOnly: true rankAverageChange: type: number format: double readOnly: true totalMatchingResults: type: integer format: int64 readOnly: true additionalProperties: false '400': description: Invalid request parameters or date range '401': description: User failed authorization '500': description: Server error while processing ranking history data tags: - Ranking History API /v2/historic/getHistoricRankingsForKeywordOnDomains: get: operationId: HistoricRanking_GetHistoricRankingsForKeywordOnDomains_GET summary: Get Keyword Domain Rankings description: 'Returns historical organic ranking data for a specific keyword across multiple specified domains within a date range. Compare how different domains have performed for the same keyword over time. [Visualize this API live on SpyFu](https://www.spyfu.com/keyword/ranking-history?query=example%20keyword) _(TODO – verify)_' parameters: - name: keyword in: query description: Keyword to analyze ranking history for. required: true schema: type: string example: best running shoes - name: domains in: query description: Comma-separated list of domains to compare rankings for. required: true schema: type: string example: example.com,competitor1.com,competitor2.com - name: startDate in: query description: Beginning date for range schema: type: string format: date-time example: 2025-01 example: 2025-01 - name: endDate in: query description: Ending date for range schema: type: string format: date-time example: 2025-07 example: 2025-07 - name: countryCode in: query description: Country market to search. Specifically, this maps to the Google domain version to query against (e.g., google.com for US, google.de for Germany, etc.). All Countries schema: type: string default: US enum: - AR - AT - AU - BE - BR - CA - CH - DE - DK - ES - FR - IE - IN - IT - JP - MX - NL - 'NO' - NZ - PL - PT - SE - SG - TR - UA - UK - US - ZA example: US example: US responses: '200': description: Successfully retrieved keyword ranking history across specified domains content: application/json: schema: type: object properties: resultCount: description: Number of results returned type: integer format: int32 readOnly: true example: 100 results: type: array items: type: object properties: domain: type: string nullable: true keyword: type: string nullable: true results: type: object additionalProperties: type: integer format: int32 nullable: true additionalProperties: false nullable: true additionalProperties: false '400': description: Invalid request parameters, keyword, or domain list '401': description: User failed authorization '500': description: Server error while processing ranking history data tags: - Ranking History API /v2/historic/getHistoricRankingsForDomainOnKeywords: get: operationId: HistoricRanking_GetHistoricRankingsForDomainOnKeywords_GET summary: Get Domain Keyword Rankings description: 'Returns historical organic ranking data for a specific domain across multiple specified keywords within a date range. Analyze how a domain''s positions have changed over time for your target keyword list. [Visualize this API live on SpyFu](https://www.spyfu.com/seo/ranking-history/domain?expandChart=false&searchType=MostValuable&includeAnyTerm=true&query=example.com&sidebarContext=topics) _(TODO – verify)_' parameters: - name: domain in: query description: Domain to analyze ranking history for. required: true schema: type: string example: example.com - name: keywords in: query description: Comma-separated list of keywords to analyze rankings for. required: true schema: type: string example: best running shoes,nike shoes,athletic footwear - name: startDate in: query description: Beginning date for range schema: type: string format: date-time example: 2025-01 example: 2025-01 - name: endDate in: query description: Ending date for range schema: type: string format: date-time example: 2025-07 example: 2025-07 - name: countryCode in: query description: Country to get results for. schema: type: string default: US enum: - AR - AT - AU - BE - BR - CA - CH - DE - DK - ES - FR - IE - IN - IT - JP - MX - NL - 'NO' - NZ - PL - PT - SE - SG - TR - UA - UK - US - ZA x-enumDescriptions: AR: Argentina AT: Austria AU: Australia BE: Belgium BR: Brazil CA: Canada CH: Switzerland DE: Germany DK: Denmark ES: Spain FR: France IE: Ireland IN: India IT: Italy JP: Japan MX: Mexico NL: Netherlands 'NO': Norway NZ: New Zealand PL: Poland PT: Portugal SE: Sweden SG: Singapore TR: Turkey UA: Ukraine UK: United Kingdom US: United States ZA: South Africa example: US example: US responses: '200': description: Successfully retrieved domain ranking history for specified keywords content: application/json: schema: type: object properties: resultCount: description: Number of results returned type: integer format: int32 readOnly: true example: 100 results: type: array items: type: object properties: domain: type: string nullable: true keyword: type: string nullable: true results: type: object additionalProperties: type: integer format: int32 nullable: true additionalProperties: false nullable: true additionalProperties: false '400': description: Invalid request parameters, domain, or keyword list '401': description: User failed authorization '500': description: Server error while processing ranking history data tags: - Ranking History API components: securitySchemes: Basic_Authentication_Token: type: http description: 'Basic Authentication is a standard that involves encoding your SPYFU_API_ID:SECRET_KEY into a Base64 string. Your SpyFu API ID and Secret Key can both be found under the Account Settings -> API Usage page. Additionally, you can find the Base64 string has been pre-generated on the same page under Base 64 Key. Finally, this encoded string is sent in the "Authorization" header prefixed with the keyword "Basic":
For example, to authorize as 00000000-0000-0000-0000-000000000000:AB12WXYZ the client would send
Authorization: Basic MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwOkFCMTJXWFla
' scheme: basic Query_Parameter_Token: type: apiKey description: An API key can be added as a query parameter. Your API key is listed as "Secret Key" found under the Account Settings -> API Usage page
For example, to authorize with the API key AB12WXY
/apis/example_api/GetExample?domain=spyfu.com&api_key=AB12WXYZ
name: api_key in: query HMAC_Authentication_Header: type: apiKey description: For even more security, each request can be individually authenticated with a timestamped HMAC (Hash Message Authentication Code) signature. Composed of your secret key, a valid timestamp, the API request path, and all request parameters.

Creating the signature:


Combine the pieces that will be converted into the signature.
StringToSign =
HTTP-Verb + "\n" +
Timestamp + "\n" +
UrlPath + "\n"
QueryParameters;

Create UTF-8 encodings of the above string and of your secret key.
byte[] SecretKeyBytes = UTF-8-Encoding-Of( Upper-Case-Of( SECRET_KEY ) );
byte[] StringToSignBytes = UTF-8-Encoding-Of( StringToSign );

Use an implementation of HMAC256 using your UTF-8 secret key to encode the combined string of your request. This should then be converted to Base64 to finish creating your signature.
Signature = Base64( HMAC-SHA256( SecretKeyBytes, StringToSignBytes ) );

Using the signature:


This signature is then sent in through an Authentication header with your username.
Authentication: UserName:Signature
name: Authentication in: header