# Payments & Credit cards Source: https://docs.keywordinsights.ai/account-management/payments-and-credit-cards/README # Add or change VAT number Source: https://docs.keywordinsights.ai/account-management/payments-and-credit-cards/add-or-change-vat-number If you want to add your VAT number to your invoices; Click the user icon in your top right-hand corner, and select -> Billing settings -> Manage billing settings. Click "Update information." Under TAX ID. Select your country. Add the VAT number. Click "Save". # Changing billing name or address Source: https://docs.keywordinsights.ai/account-management/payments-and-credit-cards/changing-billing-name-or-address If you want to change your billing name or address; Click the user icon in your top right-hand corner, and select -> Billing settings -> Manage billing settings. Update your address or name here and click Save. # Changing credit card details Source: https://docs.keywordinsights.ai/account-management/payments-and-credit-cards/changing-credit-card-details If you want to change your credit card details; Click the user icon in your top right-hand corner, and select -> Billing settings -> Manage billing settings. Click the edit icon next to your default payment method. Follow the instructions to add a new card. # Downloading invoices Source: https://docs.keywordinsights.ai/account-management/payments-and-credit-cards/downloading-invoices ### How to download an invoice? There are multiple ways to download an invoice. Click the user icon in your top right-hand corner, and select -> Invoices. Click to download invoices. Click the user icon on your top right-hand corner, select -> Billing settings -> Manage billing settings -> Download under "Invoice history." Please note that using the second method is not recommended, as Stripe will expire invoices after a certain period. Always use the first method, as we will locally host your invoices, and they will never expire. # How to add a backup payment method Source: https://docs.keywordinsights.ai/account-management/payments-and-credit-cards/how-to-add-a-backup-payment-method If you want to add a backup payment method; Click the user icon in your top right-hand corner, and select -> Billing Settings -> Manage billing settings. Click "Add payment method." Follow the instructions to add a new card. # Pricing Source: https://docs.keywordinsights.ai/account-management/pricing/README # Legacy Subscriptions Source: https://docs.keywordinsights.ai/account-management/pricing/legacy-subscriptions ### What is a Legacy Customer? A legacy customer is anyone who purchased a Keyword Insights subscription on or before February 12th, 2025. These customers maintain access to the pricing structure, features, and terms that were available at the time of their initial subscription. ### How Do I Know If I'm a Legacy Customer? You can easily verify your legacy status by following these steps: 1. Log into your Keyword Insights account 2. Navigate to "Settings" 3. Select "Subscriptions" from the menu 4. Look for text that identifies you as a legacy customer If you see a notification stating you're on a legacy plan, your subscription is grandfathered under the terms that were in place when you originally subscribed. ### What Happens If I Want to Upgrade to an Annual Subscription from My Legacy Monthly Subscription? If you're currently on a legacy subscription and wish to upgrade to an annual plan: You'll be moved to our latest plans with updated pricing and features. Please note that **legacy plans are no longer available,** so any changes will be permanent. If you cancel, you won't be able to return to this plan. Refunds are not available for any plan changes, so we recommend reviewing the new plans carefully before making a decision. ### What Happens If I Cancel My Legacy Subscription? If you decide to cancel your legacy subscription: You won't be able to return to this plan at a later date. Refunds are not available for any plan changes or cancellations. We recommend reviewing your needs and our current offerings carefully before canceling your legacy subscription. ### Will You Grandfather Me Forever? For the past 5 years, we've grandfathered all of our customers to honor our commitment to those who joined us early in our journey. However, there may be unforeseen circumstances such as massive price hikes, technical debt/limitations, etc. that could require us to revisit legacy plans. In these rare cases, we may decide to cease your grandfathered offer. Should this occur, we promise to: 1. Provide you with plenty of advance notice 2. Offer alternative solutions that aim to maintain value comparable to your legacy plan 3. Work with you to find the best path forward that meets your needs We value our legacy customers and will make every effort to continue supporting you on your current terms for as long as reasonably possible. # Subscription Source: https://docs.keywordinsights.ai/account-management/pricing/subscription/README We offer both subscription and "pay as you go" pricing options for flexibility. However, please note that our monthly subscriptions are significantly cheaper and have additional benefits. ### Subscription plans, when paid monthly ### Subscription plans, when paid annually All annual subscriptions will get a 20% discount. You can upgrade anytime. ### Detailed feature breakdown You can see a detailed feature comparison breakdown [here](https://www.keywordinsights.ai/pricing/). # Universal Credits Explained Source: https://docs.keywordinsights.ai/account-management/pricing/subscription/universal-credits-explained ## We offer a robust universal credits system that allows subscribers to access any feature within the app using their credits. ### **Monthly/Annual subscription** With a subscription, you receive X credits each month, which can be used across any feature in the app. At the end of each billing cycle, unused credits will be reset, and a fresh set of credits will be added. If you cancel your subscription, all remaining credits will expire at the end of your billing period. ### Credit top-up (For users with a Subscription) If you run out of credits before your quota is replenished, you can purchase additional credits as a top-up. These credits are available at discounted rates based on your subscription plan. ### **Pay as you go** For occasional users, we offer a pay-as-you-go option with no commitment. Purchase unlimited credits anytime, valid for 30 days. These credits can be used **exclusively for keyword clustering**. If you need search intent data, it’s available for an additional 1 credit per keyword and for rank checking it costs an additional credit. So it means you need 3 credits per keyword to cluster, classify search intent and get rank data. This is perfect for users who prefer flexibility without a monthly subscription. **Note:** Accessing features like content briefs or writer assistant requires a monthly subscription. ### How do you calculate credits for clustering? The number of required credits depends on the insights you choose and the number of keywords you upload. 1 keyword = 1 credit. If you enable search intent for your clustering, you have to pay one additional credit and one additional credit for rank checking **E.g.1,** If you upload 40,000 keywords, you will need 40,000 credits to cluster them. **E.g.2,** If you upload 40,000 keywords and select search intent alongside clustering, you will need 80,000 credits. **E.g.2,** If you upload 40,000 keywords and select search intent alongside clustering and enable rank checking, you will need 120,000 credits. Pro Tip: Even if you’re an occasional user, we recommend opting for the basic monthly subscription and topping up with additional clustering credits as needed. This approach gives you access to credits at a lower rate. # Subscription Management Source: https://docs.keywordinsights.ai/account-management/subscription-management/README # Buying a subscription Source: https://docs.keywordinsights.ai/account-management/subscription-management/buying-a-subscription ### How do I buy a monthly or annual subscription? Go to **Settings -> Subscription -> Select the monthly or annual** Select your subscription. Save 20% with an Annual Subscription! # Cancelling a subscription Source: https://docs.keywordinsights.ai/account-management/subscription-management/cancelling-a-subscription ### How do I cancel a subscription? It's straightforward to cancel a subscription. Go to Settings -> Subscription and click 'Cancel subscription." You will be asked to confirm the cancellation and fill out a short survey; your subscription will be immediately cancelled once confirmed. After your subscription is cancelled, your monthly clustering credits will be available until the end of your billing cycle. After the end of the billing cycle, these credits will be reset. All of your credits including pay-as-you-go and any top-up credits will reset at the end of your billing period or on the effective cancellation date. ### How do i delete all my data and personal information? You can download all your files and personal information by going to Account settings -> Manage personal data -> [Delete account.](https://app.keywordinsights.ai/settings/account) ### What happens after I cancel my subscription? When you cancel your subscription, it remains active until the end of the current billing period. During this time, you'll retain full access to all features. At the conclusion of the billing period, your subscription will officially end, and you will lose access to the subscription-only features. All of your credits including pay-as-you-go and any top-up credits will reset at the end of your billing period or on the effective cancellation date. ### What happens to my data after I cancel my subscription? You will have until the cancellation effective date to export and back up all your project data. On that date, all project data will be **permanently deleted** and cannot be recovered. ### How do I cancel my \$1 trial? Your \$1 trial will automatically expire after 7 days, and your account will revert to the free version. You won’t be charged or upgraded to any paid plan, and no action is needed on your part. Please note that it's your sole responsibility to back up this data, as we cannot recover it or issue you a refund. # Downgrading a subscription Source: https://docs.keywordinsights.ai/account-management/subscription-management/downgrading-a-subscription ### How do I downgrade a subscription? You can downgrade a subscription at any time. Go to Settings -> Subscriptions -> Click the "Downgrade" button. ### How does it work? For downgrades, we will move you to a lower subscription without any additional charges, and it takes effect at the end of the current billing period. i.e., suppose you're on the $299 (45K credits) plan and downgrade to the $145 package (15K credits). In that case, You will still be able to use your 45K credits until the end of your billing cycle; at the end of the billing cycle, we will reset your remaining/unused subscription clustering credits and charge you the new lower package of \$145. # Pausing a subscription Source: https://docs.keywordinsights.ai/account-management/subscription-management/pausing-a-subscription ### How do I pause a monthly subscription? It's straightforward to pause a subscription. A popup will be presented and you can choose between 1,2 or 3 months and pause your subscription. ### Can I use the tool while the subscription is paused? Once you pause the subscription you’ll still have full access to all features until the end of your current billing cycle. After that, your subscription will be paused, and all features will be unavailable until you resume. For example, if you paused your subscription on May 27th but your renewal date is June 27th, the pause will only take effect on June 27th. (You won't be charged on the June 27th) This means you’ll still have full access to your subscription and credits up until that renewal date. ### Why should I pause my subscription? Pausing your subscription lets you keep your data, settings, and account intact without being billed. ### How many times can i pause per year Pause for **up to three months** and **only twice a year**. ### What happens at the end of the pause period? Your plan will automatically resume on the scheduled unpause date, and you’ll be charged the full subscription amount immediately upon reactivation. ### Can i resume my subscription before the auto resume date? Yes you can do this anytime. Go to Settings -> Subscription and click 'Resume subscription." ### What happens if i resume my subscription before the auto resume date? When you resume your subscription manually before the auto resume, your account will be reactivated immediately, allowing you to use the application right away. However, since no payment was collected during the paused period, your credits will not be renewed until your next billing cycle. This means full access is restored instantly, but credit renewal will align with your upcoming scheduled payment. ### What happens to my subscription and PAYG credits during pause? PAYG and Top-up credits will remain frozen until the subscription is renewed. Once the subscription is reactivated and the user is successfully charged, the associated subscription credits will be added to the account. Plus the PAYG and Top-up credits unfrozen and available immediately. ### Can I pause my annual subscription? Unfortunately no. # Subscription Renewal & Payment Policy Source: https://docs.keywordinsights.ai/account-management/subscription-management/subscription-renewal-and-payment-policy This guide explains how Keyword Insights handles annual subscription renewals, payment attempts, failed charges, data deletion, and refund policy. ### 1. Automatic Renewal All Keyword Insights subscriptions are set to auto-renew by default. * Your annual plan will automatically renew on your renewal date (visible in your billing settings). * The plan and price will match your existing subscription unless you’ve made changes. * The charge will be made using your default payment method on file. Important: Please ensure your card is active and has sufficient funds before your renewal date to avoid service disruption. ### 2. Payment Attempt Schedule If the initial payment fails, our payment processor (Stripe) will retry the charge: * Up to 8 attempts * Spread over a 21-day period Each attempt will use the same card on file. You can update your payment method at any time from your account settings. ### 3. Failed Payment Notifications If a payment attempt fails, we’ll notify you via email: * First email: Immediately after the first failed attempt * Second email: 7 days after the first failure * Third email: 15 days after the first failure These reminders are designed to give you enough time to update your payment method and ensure continuity of service. ### 4. Cancellation & Data Deletion If we’re unable to successfully charge your card within the 21-day retry window: * Your subscription will be automatically cancelled on the 21st day * All of your associated data (projects, keywords, reports, etc.) will be permanently deleted Please note: Once your data is deleted, we cannot recover it. This action is **final**. ### 5. Refund Policy We operate with a strict **no refund** policy. * All payments are final. * Once the charge is successfully processed, no refunds will be issued under any circumstances. No Refunds. No Exceptions. *** # Upgrading a subscription Source: https://docs.keywordinsights.ai/account-management/subscription-management/upgrading-a-subscription ### How do I upgrade a subscription? You can Upgrade your subscription anytime. Go to Settings -> **Subscriptions** -> Click the "**Select plan**" button. ### How does it work? We will charge you the difference between the current and new packages if you upgrade your subscription. In addition, we give credit for the difference. i.e., If you're on the $58 plan (6K credits) and upgraded to the $145 plan (15K credits), we'll charge you $87 (Price difference) and add 9K credits (credit difference), and we will move you to the new subscription so your next bill is on the $145 plan. # Team Management Source: https://docs.keywordinsights.ai/account-management/team-management/README We understand the importance of Teamwork, and unlike other tools, we don't want to tie you into expensive team plans. Even our basic plan comes with 3 user seats. ### How to invite a team member? Go to Settings -> Organisation settings -> Under "Add team members" Add your teammates' email addresses and click "Send invite" Your teammate will receive an email and click the Authenticate button to gain access to your organisation. ### Can a team member access my billing details? The answer is no. Your team members can use your credits and cannot upgrade/downgrade or access billing details. Only the admin can access these features. ### How do I remove a team member? Click the "Delete" icon to remove a member. # Adding a Team Member Source: https://docs.keywordinsights.ai/account-management/team-management/adding-a-team-member We understand the importance of teamwork, and our system is built so its easier to invite, share and manage your team members. ### How do I invite a team member? Click the user icon in your top right-hand corner, and select -> Team settings. This is your team's control panel. You can invite new users to your account from the Add team members section. There are 2 ways to invite team members. 1. Adding your team member's email. 2. Sending an invite link to your team member. Input your team members' email and click the 'Member' drop-down menu. Select the privilege and click "Send invite." There are 3 types of privileges. 1. Member - A regular team member who can access their own projects and any shared/allowed projects. 2. Manager - A manager can manage team members and has full access to team reports, team projects and project-sharing settings. 3. Administrator - An administrator can access everything and share and manage managers and team members. ### How can I add additional team members? Depending on your subscription, you will have an allocation of user seats. | Basic | Professional | Premium | | ----------- | ------------ | ------------ | | 1 user seat | 3 user seat | 5 user seats | You can add additional team members by buying a seat at \$10 /mo. Click the user icon in your top right-hand corner, and select -> Team settings. Click "**Add seats**" Select the number of seats. Click "**Buy**" # Sharing Projects Source: https://docs.keywordinsights.ai/account-management/team-management/sharing-projects You can share projects and searches with your team members and collaborate effectively. ### How do I share a project? Go to Projects, click 'Team' from the second tab. Navigate to any project and select the project you want to share. Click the toggle under the Team Access column. You can also share all of the projects in bulk by going to profile -> Team settings. Under the Team section, toggle "**All members have access to team Projects**" This will instantly share all of your projects with your team members. Please note the Team sharing feature is only available on the **professional or above plan**. # Sharing Team Reports Source: https://docs.keywordinsights.ai/account-management/team-management/sharing-team-reports Need to manage a large team and track their collective usage? Our comprehensive team reports have got you covered! ### How do I use Team reports? Go to projects, click 'Team', and now click 'Team reports.' This report will show all the features used, who used it and how often. Click filters to refine your search further. Please note the Team reports feature is only available on the **premium plan**. # User seats Source: https://docs.keywordinsights.ai/account-management/team-management/user-seats With our user seats feature you can invite you entire team and collaborate on your projects. ### How do i buy seats? Go to Settings -> Click the + icon next to seats, select the number of seats and click buy. ### What is the cost for user seats? Each seat costs \$10 per month. So if you buy 2 seats, it will be an additional \$20 a month. Tip: Upgrading to a higher plan with more seats can often be more cost-effective than purchasing additional seats individually. Visit our pricing comparison [page](https://www.keywordinsights.ai/pricing/) for full details. # API Key Authentication Source: https://docs.keywordinsights.ai/api/api-key-authentication Create and use API keys to authenticate with the Keyword Insights API API keys provide a simple, long-lived way to authenticate with the Keyword Insights public API. Unlike Bearer tokens (which expire), API keys remain valid until you delete them — making them ideal for scripts, integrations, and automated workflows. API key access is available on **Professional** and **Premium** plans. If you're on a different plan, you'll be prompted to upgrade when attempting to create an API key. ### Creating an API Key 1. Log in to your [Keyword Insights dashboard](https://app.keywordinsights.ai). 2. Navigate to **API Keys** from the left sidebar menu. 3. Click the **Create API key** button. 4. Enter a descriptive name for the key (e.g. "Python scripts", "N8N integration"). 5. Click **Create**. Once created, your API key will be displayed **once**. Copy it immediately and store it in a secure location — you will not be able to see the full key again. Your key will look like this: ``` kwi_sk_aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890abc ``` After you close the modal, the key list will show only the prefix (e.g. `kwi_sk_aBcDe...`) along with the creation date and last used date. ### Using the API Key Pass your API key in the `X-API-Key` header with every request: ```python Python theme={null} import requests API_KEY = "kwi_sk_your_api_key_here" BASE_URL = "https://api.keywordinsights.ai" response = requests.get( f"{BASE_URL}/api/user/", headers={"X-API-Key": API_KEY}, ) print(response.json()) ``` ```javascript JavaScript theme={null} const API_KEY = "kwi_sk_your_api_key_here"; const BASE_URL = "https://api.keywordinsights.ai"; async function getUser() { const response = await fetch(`${BASE_URL}/api/user/`, { headers: { "X-API-Key": API_KEY }, }); const data = await response.json(); console.log(data); } getUser(); ``` Treat your API key like a password. Do not commit it to version control or share it publicly. Use environment variables to store it securely. ### Storing the Key Securely We recommend loading your API key from an environment variable: ```bash theme={null} export KWI_API_KEY="kwi_sk_your_api_key_here" ``` Then in Python: ```python Python theme={null} import os API_KEY = os.environ["KWI_API_KEY"] ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; ``` ### Example: Create a Clustering Order ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" payload = { "project_name": "My clustering project", "keywords": [ "keyword clustering", "keyword research", "topical authority", ], "search_volumes": [2300, 3210, 5500], "language": "en", "location": "United States", "device": "desktop", "clustering_method": "volume", "grouping_accuracy": 4, "hub_creation_method": "medium", "insights": ["cluster", "rank", "context"], "url": "https://example.com", "folder_id": "", } response = requests.post( f"{BASE_URL}/api/keywords-insights/order/", headers={"X-API-Key": API_KEY}, json=payload, ) order = response.json() print(order) ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const payload = { project_name: "My clustering project", keywords: [ "keyword clustering", "keyword research", "topical authority", ], search_volumes: [2300, 3210, 5500], language: "en", location: "United States", device: "desktop", clustering_method: "volume", grouping_accuracy: 4, hub_creation_method: "medium", insights: ["cluster", "rank", "context"], url: "https://example.com", folder_id: "", }; async function createOrder() { const response = await fetch(`${BASE_URL}/api/keywords-insights/order/`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify(payload), }); const order = await response.json(); console.log(order); } createOrder(); ``` The response will contain an `order_id` that you'll use to retrieve results once the order is processed. ### Managing API Keys You can manage your API keys from the dashboard at any time: * **View keys** — see all active keys with their prefix, creation date, and last used date. * **Delete keys** — revoke a key immediately. Any application using that key will lose access. To delete a key, click the trash icon next to the key and confirm the deletion. Deleting an API key is immediate and permanent. Make sure no active integrations depend on the key before deleting it. ### API Key vs. Bearer Token (Deprecated) | | API Key (Recommended) | Bearer Token (Deprecated) | | ----------------- | --------------------------------- | ----------------------------------- | | **How to obtain** | Dashboard → API Keys | Login endpoint or browser dev tools | | **Header** | `X-API-Key: kwi_sk_...` | `Authorization: Bearer eyJ...` | | **Expires** | Never (until deleted) | After a set period | | **Best for** | Scripts, integrations, automation | — | **Bearer token authentication is deprecated.** If you are currently using Bearer tokens, we strongly recommend migrating to API keys. Bearer tokens will continue to work for now, but may be removed in a future update. ### Migrating from Bearer Tokens If you have existing code using Bearer tokens, switching to API keys is a one-line change — replace the `Authorization` header with `X-API-Key`: ```python Python theme={null} # Before (deprecated) headers = {"Authorization": f"Bearer {JWT_TOKEN}"} # After (recommended) headers = {"X-API-Key": API_KEY} ``` ```javascript JavaScript theme={null} // Before (deprecated) const headers = { "Authorization": `Bearer ${JWT_TOKEN}` }; // After (recommended) const headers = { "X-API-Key": API_KEY }; ``` No other changes are needed — all API endpoints accept both authentication methods. # API Use Cases Source: https://docs.keywordinsights.ai/api/api-use-cases/README This is the hub for all use cases for the Keyword Insights public API. Each page includes basic request examples, supported parameters, customization options, and guidance on retrieving results. View the OpenAPI routes here: [https://api.keywordinsights.ai/apidocs/](https://api.keywordinsights.ai/apidocs/) ### Public API use cases * [Clustering](/api/api-use-cases/public-api-clustering) — group keywords into topical clusters * [Content Brief](/api/api-use-cases/public-api-content-brief) — generate structured briefs and outlines * [Writer Agent](/api/api-use-cases/public-api-writer-agent) — generate long-form content drafts * [Advanced Ranking](/api/api-use-cases/public-api-advanced-ranking) — run advanced SERP-based ranking analysis * [Keyword Content](/api/api-use-cases/public-api-keyword-content) — build content based on keyword inputs When using the public API, create an API key and include it with every request. See [API Key Authentication](/api/api-key-authentication) to get started. # N8N integration Source: https://docs.keywordinsights.ai/api/api-use-cases/n8n-integration This guide uses **Bearer token** authentication, which is deprecated. For new N8N setups, you can skip the login step entirely by using an **API key** in the `X-API-Key` header instead. See [API Key Authentication](/api/api-key-authentication). On this page, you'll find the guide on how to connect Keyword Insights API with N8N to run clustering. ### Step 1 #### Authentication Use an HTTP Requst node, ensure the route is correct \`[https://api.keywordinsights.ai//authentication/login/](https://api.keywordinsights.ai/authentication/login/)\`and the method is "POST".Finally, enable the body option, and enable JSON type, and pass the following by replacing the values of your account email/password. ``` { "email": "", "password": "" } ``` ### Step 2 #### Creating a clustering order Ensure the output of the Auth node shows in the second node. Update the following settings: * Method -> POST * URL -> [https://api.keywordinsights.ai/api/keywords-insights/order/](https://api.keywordinsights.ai/api/keywords-insights/order/) * Send Headers -> Enabled * Header Parameter Key -> Authorization * Header Paramater Value -> Bearer `{'{ $json.result.access_token }'}` * Send Body -> Enabled * JSON, Using JSON Paste the following json into the body, make sure to update the keywords, volumes you want to cluster, and URL, you can also customize further based on the configuration you want. You can also pass `folder_id` if you want it placed in a specific folder in the platform. ``` { "clustering_method": "volume", "device": "desktop", "grouping_accuracy": 4, "hub_creation_method": "medium", "insights": [ "cluster", "rank", "context" ], "language": "en", "location": "United States", "project_name": "A Clustering project", "keywords": [ "keyword clustering", "keyword research", "topical authority", "clustering methods", "page authority" ], "search_volumes": [ 2300, 3210, 5500, 2100, 1200 ], "url": "https://www.keywordinsights.ai/" } ``` That's all! # Public API: Advanced Ranking Source: https://docs.keywordinsights.ai/api/api-use-cases/public-api-advanced-ranking Analyze SERP rankings for your domain with top URLs, word counts, and related searches via the API Analyze how a domain ranks for any keyword — get top SERP URLs, domain positions, word counts, and related searches. Full endpoint reference available on [Swagger](https://api.keywordinsights.ai/apidocs/#/Keyword%20Ranking). ### Quick Start Check how a domain ranks for a single keyword: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} payload = { "keyword": "project management software", "domain": "asana.com", "language": "en", "location": "United States", } response = requests.post( f"{BASE_URL}/api/advanced-ranking/order/", headers=HEADERS, json=payload, ) data = response.json() print(data) # {"status": true, "order_id": "2879fa0b-...", "cost": 1} ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY, "Content-Type": "application/json" }; const payload = { keyword: "project management software", domain: "asana.com", language: "en", location: "United States", }; async function createOrder() { const response = await fetch(`${BASE_URL}/api/advanced-ranking/order/`, { method: "POST", headers: HEADERS, body: JSON.stringify(payload), }); const data = await response.json(); console.log(data); // {"status": true, "order_id": "2879fa0b-...", "cost": 1} } createOrder(); ``` ### Endpoints | Method | Path | Description | | ------ | -------------------------------------------------- | ------------------------------- | | POST | `/api/advanced-ranking/order/` | Single keyword ranking analysis | | GET | `/api/advanced-ranking/order/?order_id={id}` | Get single keyword results | | POST | `/api/advanced-ranking/batch/order/` | Batch keyword ranking analysis | | GET | `/api/advanced-ranking/batch/order/?order_id={id}` | Get batch results | ### Parameters #### Single Keyword — Required | Parameter | Type | Description | | ---------- | ------ | ----------------------------------------------------------------------------- | | `keyword` | string | Target keyword to analyze | | `domain` | string | Domain to track rankings for (e.g. `asana.com`) | | `language` | string | Language code (e.g. `en`). See `/api/keywords-insights/languages/` | | `location` | string | Location name (e.g. `United States`). See `/api/keywords-insights/locations/` | #### Single Keyword — Optional | Parameter | Type | Default | Description | | -------------------- | ------- | --------- | ----------------------------------------------------------------------- | | `device` | string | `desktop` | `desktop` or `mobile` | | `include_word_count` | boolean | `false` | Include word count for top ranking pages. Costs 10 credits instead of 1 | #### Batch — Required | Parameter | Type | Description | | ---------- | --------- | ---------------------------- | | `keywords` | string\[] | Array of keywords to analyze | | `domain` | string | Domain to track rankings for | | `language` | string | Language code | | `location` | string | Location name | #### Batch — Optional Same as single keyword: `device`, `include_word_count`. ### Cost | Mode | Without word count | With word count | | -------------- | -------------------- | ---------------------- | | Single keyword | 1 credit | 10 credits | | Batch | 1 credit per keyword | 10 credits per keyword | ### Customizations #### Including Word Count Enable `include_word_count` to get word counts for the top 10 ranking URLs. Useful for benchmarking content length: ```python Python theme={null} payload = { "keyword": "best CRM software", "domain": "hubspot.com", "language": "en", "location": "United States", "include_word_count": True, # 10 credits instead of 1 } ``` ```javascript JavaScript theme={null} const payload = { keyword: "best CRM software", domain: "hubspot.com", language: "en", location: "United States", include_word_count: true, // 10 credits instead of 1 }; ``` #### Batch Ranking Analyze multiple keywords at once: ```python Python theme={null} payload = { "keywords": [ "project management software", "best project management tools", "project management for teams", ], "domain": "asana.com", "language": "en", "location": "United States", } response = requests.post( f"{BASE_URL}/api/advanced-ranking/batch/order/", headers=HEADERS, json=payload, ) data = response.json() print(f"Batch order: {data['order_id']} (cost: {data['cost']} credits)") ``` ```javascript JavaScript theme={null} const payload = { keywords: [ "project management software", "best project management tools", "project management for teams", ], domain: "asana.com", language: "en", location: "United States", }; async function createBatchOrder() { const response = await fetch(`${BASE_URL}/api/advanced-ranking/batch/order/`, { method: "POST", headers: HEADERS, body: JSON.stringify(payload), }); const data = await response.json(); console.log(`Batch order: ${data.order_id} (cost: ${data.cost} credits)`); } createBatchOrder(); ``` ### Retrieving Results Poll until the order status is `"done"`: ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717" while True: response = requests.get( f"{BASE_URL}/api/advanced-ranking/order/", headers=HEADERS, params={"order_id": order_id}, ) data = response.json()["result"]["payload"] if data["status"] == "done": break print("Processing...") time.sleep(10) results = data["results"] print(f"Domain URLs in top 100: {results['n_domain_rankings']}") for ranking in results["domain_rankings"]: print(f" #{ranking['rank']} — {ranking['url']}") print(f"\nTop 10 SERP results:") for url_data in results["top_rankings"]: wc = f" ({url_data['word_count']} words)" if url_data.get("word_count") else "" print(f" {url_data['url']}{wc}") print(f"\nRelated searches: {results['related_searches']}") print(f"People Also Ask: {results['related_questions']}") ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY }; const order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"; async function getResults() { let data; while (true) { const url = new URL(`${BASE_URL}/api/advanced-ranking/order/`); url.searchParams.append("order_id", order_id); const response = await fetch(url, { headers: HEADERS, }); const json = await response.json(); data = json.result.payload; if (data.status === "done") { break; } console.log("Processing..."); await new Promise((r) => setTimeout(r, 10000)); } const results = data.results; console.log(`Domain URLs in top 100: ${results.n_domain_rankings}`); for (const ranking of results.domain_rankings) { console.log(` #${ranking.rank} — ${ranking.url}`); } console.log("\nTop 10 SERP results:"); for (const url_data of results.top_rankings) { const wc = url_data.word_count ? ` (${url_data.word_count} words)` : ""; console.log(` ${url_data.url}${wc}`); } console.log(`\nRelated searches: ${results.related_searches}`); console.log(`People Also Ask: ${results.related_questions}`); } getResults(); ``` The results include: | Field | Description | | ------------------- | ------------------------------------------------------------------------- | | `domain_rankings` | List of your domain's URLs that rank in the top 100, with their positions | | `n_domain_rankings` | Total number of your domain's URLs in the top 100 | | `top_rankings` | Top 10 SERP URLs (with optional word counts) | | `related_searches` | Related searches from the SERP | | `related_questions` | People Also Ask questions from the SERP | ### Complete Example Analyze a domain's rankings for multiple keywords and summarize the results: ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} def create_batch(keywords, domain, language="en", location="United States"): """Create a batch ranking order.""" response = requests.post( f"{BASE_URL}/api/advanced-ranking/batch/order/", headers=HEADERS, json={ "keywords": keywords, "domain": domain, "language": language, "location": location, "include_word_count": True, }, ) response.raise_for_status() return response.json() def wait_for_batch(order_id): """Poll until batch is complete.""" while True: response = requests.get( f"{BASE_URL}/api/advanced-ranking/batch/order/", headers=HEADERS, params={"order_id": order_id}, ) data = response.json()["result"]["payload"] if data["status"] == "done": return data["results"] print("Processing...") time.sleep(15) # --- Run --- keywords = [ "project management software", "task management tool", "team collaboration software", ] result = create_batch(keywords, domain="asana.com") order_id = result["order_id"] print(f"Batch created: {order_id} (cost: {result['cost']} credits)") results = wait_for_batch(order_id) for item in results: print(f"\n'{item['keyword']}': {item['n_domain_rankings']} URLs in top 100") for r in item["domain_rankings"][:3]: print(f" #{r['rank']} — {r['url']}") ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY, "Content-Type": "application/json" }; async function createBatch(keywords, domain, language = "en", location = "United States") { const response = await fetch(`${BASE_URL}/api/advanced-ranking/batch/order/`, { method: "POST", headers: HEADERS, body: JSON.stringify({ keywords, domain, language, location, include_word_count: true, }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } async function waitForBatch(order_id) { while (true) { const url = new URL(`${BASE_URL}/api/advanced-ranking/batch/order/`); url.searchParams.append("order_id", order_id); const response = await fetch(url, { headers: HEADERS, }); const json = await response.json(); const data = json.result.payload; if (data.status === "done") { return data.results; } console.log("Processing..."); await new Promise((r) => setTimeout(r, 15000)); } } // --- Run --- async function run() { const keywords = [ "project management software", "task management tool", "team collaboration software", ]; const result = await createBatch(keywords, "asana.com"); const order_id = result.order_id; console.log(`Batch created: ${order_id} (cost: ${result.cost} credits)`); const results = await waitForBatch(order_id); for (const item of results) { console.log(`\n'${item.keyword}': ${item.n_domain_rankings} URLs in top 100`); for (const r of item.domain_rankings.slice(0, 3)) { console.log(` #${r.rank} — ${r.url}`); } } } run(); ``` # Public API: Clustering Source: https://docs.keywordinsights.ai/api/api-use-cases/public-api-clustering Create keyword clustering orders, check status, and retrieve results via the API Cluster keywords by SERP similarity, detect search intent, and track rankings — all programmatically. Full endpoint reference available on [Swagger](https://api.keywordinsights.ai/apidocs/#/Keyword%20Insights). ### Quick Start Create a clustering order with the minimum required parameters: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} payload = { "project_name": "My first cluster", "keywords": ["keyword clustering", "keyword grouping", "cluster keywords"], "search_volumes": [2300, 1200, 900], "language": "en", "location": "United States", "insights": ["cluster", "context"], "folder_id": "", } response = requests.post( f"{BASE_URL}/api/keywords-insights/order/", headers=HEADERS, json=payload, ) data = response.json() print(data) # {"status": true, "order_id": "2879fa0b-...", "cost": 150} ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY, "Content-Type": "application/json", }; const payload = { project_name: "My first cluster", keywords: ["keyword clustering", "keyword grouping", "cluster keywords"], search_volumes: [2300, 1200, 900], language: "en", location: "United States", insights: ["cluster", "context"], folder_id: "", }; async function createOrder() { const response = await fetch(`${BASE_URL}/api/keywords-insights/order/`, { method: "POST", headers: HEADERS, body: JSON.stringify(payload), }); const data = await response.json(); console.log(data); } createOrder(); ``` ### Endpoints | Method | Path | Description | | ------ | -------------------------------------------------------------------- | ------------------------------- | | POST | `/api/keywords-insights/order/` | Create a clustering order | | GET | `/api/keywords-insights/order/?order_id={id}` | Check order status | | GET | `/api/keywords-insights/order/json/{order_id}/` | Get results as JSON (paginated) | | GET | `/api/keywords-insights/order/xlsx/{order_id}/` | Export results as XLSX | | GET | `/api/keywords-insights/orders/?n_orders={n}` | List recent orders | | GET | `/api/keywords-insights/order/cost/?n_keywords={n}&insights=cluster` | Estimate cost | | GET | `/api/keywords-insights/languages/` | Supported languages | | GET | `/api/keywords-insights/locations/` | Supported locations | ### Parameters #### Required | Parameter | Type | Description | | ---------------- | --------- | ------------------------------------------------------------------------------------------------ | | `project_name` | string | Name shown in the dashboard | | `keywords` | string\[] | List of keywords to cluster (min 5, max 200,000) | | `search_volumes` | number\[] | Search volume per keyword (same length as `keywords`) | | `language` | string | Language code (e.g. `en`). See `/api/keywords-insights/languages/` | | `location` | string | Location name (e.g. `United States`). See `/api/keywords-insights/locations/` | | `insights` | string\[] | Insight types: `cluster`, `context`, `rank` | | `folder_id` | string | Dashboard folder ID to save the project into. Retrieve from the browser URL in the KWI dashboard | #### Optional | Parameter | Type | Default | Description | | --------------------- | ------- | --------- | ----------------------------------------------------------------- | | `clustering_method` | string | `volume` | `volume` or `agglomerative` | | `grouping_accuracy` | integer | `4` | SERP overlap threshold (1–7). Higher = stricter clusters | | `hub_creation_method` | string | `medium` | Topical cluster similarity: `soft`, `medium`, `hard` | | `device` | string | `desktop` | SERP device: `desktop`, `mobile`, `tablet` | | `mobile_type` | string | — | `iphone` or `android` (when `device` is `mobile`) | | `tablet_type` | string | — | `ipad` or `android` (when `device` is `tablet`) | | `url` | string | — | Domain URL for ranking. **Required** when `rank` is in `insights` | ### Insight Types The `insights` array controls what analysis runs: * **`cluster`** — Groups keywords by SERP similarity. **Required** for all clustering orders. * **`context`** — Detects search intent (informational, commercial, transactional, navigational). * **`rank`** — Tracks your domain's ranking for each keyword. Requires the `url` parameter. **Full clustering order:** ```json theme={null} "insights": ["cluster", "rank", "context"] ``` **Intent-only order** (no clustering, just intent classification): ```json theme={null} "insights": ["context"] ``` ### Customizations #### Clustering Method * **`volume`** (default) — Groups keywords based on shared SERP URLs. Controlled by `grouping_accuracy` (1 = loose, 7 = strict). Best for most use cases. * **`agglomerative`** — Uses hierarchical clustering for larger keyword sets. Better for discovering broader topic relationships. #### Topical Clusters The `hub_creation_method` controls how tightly related keywords must be to form a topical cluster: * **`soft`** — Broad grouping, more keywords per topical cluster * **`medium`** — Balanced (recommended) * **`hard`** — Strict grouping, fewer but more focused clusters ### Checking Order Status Orders process asynchronously. Poll the status endpoint until `status` is `"done"`: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717" response = requests.get( f"{BASE_URL}/api/keywords-insights/order/", headers=HEADERS, params={"order_id": order_id}, ) data = response.json() print(data["status"]) # "confirmed", "processing", or "done" print(data["progress"]) # 0.0 to 1.0 ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY }; const orderId = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"; async function checkStatus() { const url = new URL(`${BASE_URL}/api/keywords-insights/order/`); url.searchParams.append("order_id", orderId); const response = await fetch(url, { headers: HEADERS }); const data = await response.json(); console.log(data.status); // "confirmed", "processing", or "done" console.log(data.progress); // 0.0 to 1.0 } checkStatus(); ``` When the order is complete, the response includes download links: ```json theme={null} { "status": "done", "progress": 1.0, "order_id": "2879fa0b-...", "results_files": { "xlsx": "https://api.keywordinsights.ai/api/keywords-insights/order/xlsx/2879fa0b-.../", "google_sheets": "https://docs.google.com/spreadsheets/d/.../copy" } } ``` ### Retrieving Results as JSON Get paginated cluster data: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717" response = requests.get( f"{BASE_URL}/api/keywords-insights/order/json/{order_id}/", headers=HEADERS, params={ "page_size": 50, "page_number": 1, "sort_by": "search_volume", "ascending": False, }, ) data = response.json() clusters = data["result"]["payload"]["clusters"] for cluster in clusters: print(f"{cluster['name']} — {cluster['number_of_keywords']} keywords, vol: {cluster['search_volume']}") ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY }; const orderId = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"; async function getResults() { const url = new URL(`${BASE_URL}/api/keywords-insights/order/json/${orderId}/`); url.searchParams.append("page_size", "50"); url.searchParams.append("page_number", "1"); url.searchParams.append("sort_by", "search_volume"); url.searchParams.append("ascending", "false"); const response = await fetch(url, { headers: HEADERS }); const data = await response.json(); const clusters = data.result.payload.clusters; clusters.forEach((cluster) => { console.log( `${cluster.name} — ${cluster.number_of_keywords} keywords, vol: ${cluster.search_volume}` ); }); } getResults(); ``` **JSON results query parameters:** | Parameter | Type | Default | Description | | ------------- | ------- | --------------- | ---------------------------------------------------------- | | `page_size` | integer | 50 | Results per page (max 1,000) | | `page_number` | integer | 1 | Page number | | `sort_by` | string | `search_volume` | Sort field (e.g. `search_volume`, `keyword`, `cluster_id`) | | `ascending` | boolean | `false` | Sort direction | | `filter_id` | string | — | Filter ID from the filters endpoint | ### Exporting as XLSX Download results as an Excel file: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717" response = requests.get( f"{BASE_URL}/api/keywords-insights/order/xlsx/{order_id}/", headers=HEADERS, ) # Save the XLSX file with open("clusters.xlsx", "wb") as f: f.write(response.content) ``` ```javascript JavaScript theme={null} const fs = require("fs"); const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY }; const orderId = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"; async function downloadXlsx() { const response = await fetch(`${BASE_URL}/api/keywords-insights/order/xlsx/${orderId}/`, { headers: HEADERS, }); const buffer = await response.arrayBuffer(); fs.writeFileSync("clusters.xlsx", Buffer.from(buffer)); } downloadXlsx(); ``` ### Estimating Cost Check how many credits an order will cost before creating it: ```python Python theme={null} response = requests.get( f"{BASE_URL}/api/keywords-insights/order/cost/", headers=HEADERS, params={"n_keywords": 500, "insights": ["cluster", "context"]}, ) print(response.json()) # {"cost": 1000} ``` ```javascript JavaScript theme={null} async function estimateCost() { const url = new URL(`${BASE_URL}/api/keywords-insights/order/cost/`); url.searchParams.append("n_keywords", "500"); url.searchParams.append("insights", "cluster"); url.searchParams.append("insights", "context"); const response = await fetch(url, { headers: HEADERS }); const data = await response.json(); console.log(data); // { cost: 1000 } } estimateCost(); ``` **Cost per keyword by insight combination:** | Insights | Cost per keyword | | ------------------------------ | ---------------- | | `cluster` only | 1 credit | | `cluster` + `context` | 2 credits | | `cluster` + `rank` | 2 credits | | `cluster` + `context` + `rank` | 3 credits | ### Complete Example End-to-end workflow: create an order, wait for completion, and download results. ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} def create_order(keywords, volumes): """Create a clustering order.""" response = requests.post( f"{BASE_URL}/api/keywords-insights/order/", headers=HEADERS, json={ "project_name": "API automation example", "keywords": keywords, "search_volumes": volumes, "language": "en", "location": "United States", "insights": ["cluster", "context"], "folder_id": "", "clustering_method": "volume", "grouping_accuracy": 4, "hub_creation_method": "medium", }, ) response.raise_for_status() return response.json() def wait_for_completion(order_id): """Poll until the order is done.""" while True: response = requests.get( f"{BASE_URL}/api/keywords-insights/order/", headers=HEADERS, params={"order_id": order_id}, ) data = response.json() print(f"Status: {data['status']} ({data['progress']:.0%})") if data["status"] == "done": return data time.sleep(30) def get_results(order_id): """Fetch all clusters as JSON.""" response = requests.get( f"{BASE_URL}/api/keywords-insights/order/json/{order_id}/", headers=HEADERS, params={"page_size": 100, "page_number": 1}, ) response.raise_for_status() return response.json() # --- Run --- keywords = ["best running shoes", "running shoes review", "top sneakers for running"] volumes = [12000, 8500, 3200] result = create_order(keywords, volumes) order_id = result["order_id"] print(f"Order created: {order_id} (cost: {result['cost']} credits)") wait_for_completion(order_id) data = get_results(order_id) for cluster in data["result"]["payload"]["clusters"]: print(f" {cluster['name']} — {cluster['number_of_keywords']} kw, vol: {cluster['search_volume']}") ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY }; async function createOrder(keywords, volumes) { const response = await fetch(`${BASE_URL}/api/keywords-insights/order/`, { method: "POST", headers: { ...HEADERS, "Content-Type": "application/json" }, body: JSON.stringify({ project_name: "API automation example", keywords, search_volumes: volumes, language: "en", location: "United States", insights: ["cluster", "context"], folder_id: "", clustering_method: "volume", grouping_accuracy: 4, hub_creation_method: "medium", }), }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); return await response.json(); } async function waitForCompletion(orderId) { while (true) { const url = new URL(`${BASE_URL}/api/keywords-insights/order/`); url.searchParams.append("order_id", orderId); const response = await fetch(url, { headers: HEADERS }); const data = await response.json(); console.log(`Status: ${data.status} (${(data.progress * 100).toFixed(0)}%)`); if (data.status === "done") return data; await new Promise((r) => setTimeout(r, 30000)); } } async function getResults(orderId) { const url = new URL(`${BASE_URL}/api/keywords-insights/order/json/${orderId}/`); url.searchParams.append("page_size", "100"); url.searchParams.append("page_number", "1"); const response = await fetch(url, { headers: HEADERS }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); return await response.json(); } async function run() { const keywords = ["best running shoes", "running shoes review", "top sneakers for running"]; const volumes = [12000, 8500, 3200]; const result = await createOrder(keywords, volumes); const orderId = result.order_id; console.log(`Order created: ${orderId} (cost: ${result.cost} credits)`); await waitForCompletion(orderId); const data = await getResults(orderId); data.result.payload.clusters.forEach((cluster) => { console.log( ` ${cluster.name} — ${cluster.number_of_keywords} kw, vol: ${cluster.search_volume}` ); }); } run(); ``` # Public API: Content Brief Source: https://docs.keywordinsights.ai/api/api-use-cases/public-api-content-brief Generate AI-powered content briefs, retrieve SERP analysis, and create outlines via the API Generate comprehensive content briefs with SERP-based headings, AI title/description suggestions, and word count benchmarks — all programmatically. Full endpoint reference available on [Swagger](https://api.keywordinsights.ai/apidocs/#/Content%20Brief). ### Quick Start Create a content brief for a target keyword: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} payload = { "keyword": "content marketing strategies", "language": "en", "location": "United States", "folder_id": "", } response = requests.post( f"{BASE_URL}/api/content-brief/order/", headers=HEADERS, json=payload, ) data = response.json() print(data) # {"status": true, "payload": {"id": "339ca10b-...", "status": true, "cost": 500}} ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const payload = { keyword: "content marketing strategies", language: "en", location: "United States", folder_id: "", }; const response = await fetch(`${BASE_URL}/api/content-brief/order/`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify(payload), }); const data = await response.json(); console.log(data); // {"status": true, "payload": {"id": "339ca10b-...", "status": true, "cost": 500}} ``` ### Endpoints | Method | Path | Description | | ------ | -------------------------------------------------------------------------- | --------------------------- | | POST | `/api/content-brief/order/` | Create a content brief | | GET | `/api/content-brief/order/?id={id}` | Get brief results | | DELETE | `/api/content-brief/order/?ids={id1}&ids={id2}` | Delete briefs | | GET | `/api/content-brief/orders/?page=1&page_size=10` | List all briefs (paginated) | | GET | `/api/content-brief/languages/` | Supported languages | | POST | `/api/content-brief/order/{order_id}/outline/` | Generate AI outline | | GET | `/api/content-brief/order/{order_id}/outline/?auto_generate_order_id={id}` | Get AI outline results | ### Parameters #### Create Brief — Required | Parameter | Type | Description | | ----------- | ------ | ------------------------------------------------------------------------------------------------ | | `keyword` | string | Target keyword or topic | | `language` | string | Language code (e.g. `en`). See `/api/content-brief/languages/` | | `location` | string | Location name (e.g. `United States`). See `/api/keywords-insights/locations/` | | `folder_id` | string | Dashboard folder ID to save the project into. Retrieve from the browser URL in the KWI dashboard | ### Retrieving Results Content briefs process asynchronously. Poll the results endpoint until the data is ready: ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} order_id = "339ca10b-80c1-4248-b8d0-c11377c204c7" while True: response = requests.get( f"{BASE_URL}/api/content-brief/order/", headers=HEADERS, params={"id": order_id}, ) data = response.json()["result"]["payload"] if data["status"]: break print("Processing...") time.sleep(15) # Brief is ready print(f"Keyword: {data['keyword']}") print(f"Avg word count: {data['word_count_avg']}") print(f"Recommended: {data['word_count_min']}–{data['word_count_max']} words") print(f"Pages analyzed: {data['pages_count']}") print(f"Headings found: {data['headings_count']}") ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const order_id = "339ca10b-80c1-4248-b8d0-c11377c204c7"; let data; while (true) { const response = await fetch(`${BASE_URL}/api/content-brief/order/?id=${order_id}`, { headers: { "X-API-Key": API_KEY }, }); const json = await response.json(); data = json.result.payload; if (data.status) { break; } console.log("Processing..."); await new Promise((r) => setTimeout(r, 15000)); } // Brief is ready console.log(`Keyword: ${data.keyword}`); console.log(`Avg word count: ${data.word_count_avg}`); console.log(`Recommended: ${data.word_count_min}–${data.word_count_max} words`); console.log(`Pages analyzed: ${data.pages_count}`); console.log(`Headings found: ${data.headings_count}`); ``` The results include: | Field | Description | | ----------------------------------- | ---------------------------------------------------------------------- | | `raw` | SERP page structures with headings, bullet points, and content weights | | `brief_title_suggests` | AI-generated meta title suggestions | | `brief_description_suggests` | AI-generated meta description suggestions | | `word_count_avg` | Average word count across top-ranking pages | | `word_count_min` / `word_count_max` | Recommended word count range | | `pages_count` | Number of SERP pages analyzed | | `headings_count` | Total headings scraped from top results | | `processing_status` | Status of each processing stage | ### Generating an AI Outline After a content brief is complete, you can generate an AI-powered outline: ```python Python theme={null} # Step 1: Trigger outline generation response = requests.post( f"{BASE_URL}/api/content-brief/order/{order_id}/outline/", headers=HEADERS, json={"additional_context": "Focus on B2B companies"}, # optional ) auto_id = response.json()["result"]["payload"]["auto_generate_order_id"] # Step 2: Poll until outline is ready while True: response = requests.get( f"{BASE_URL}/api/content-brief/order/{order_id}/outline/", headers=HEADERS, params={"auto_generate_order_id": auto_id}, ) data = response.json()["result"]["payload"] if data["status"]: break time.sleep(10) print(data["string"]) # Full outline as text ``` ```javascript JavaScript theme={null} // Step 1: Trigger outline generation const response = await fetch(`${BASE_URL}/api/content-brief/order/${order_id}/outline/`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ additional_context: "Focus on B2B companies" }), // optional }); const json = await response.json(); const auto_id = json.result.payload.auto_generate_order_id; // Step 2: Poll until outline is ready let data; while (true) { const response = await fetch( `${BASE_URL}/api/content-brief/order/${order_id}/outline/?auto_generate_order_id=${auto_id}`, { headers: { "X-API-Key": API_KEY }, } ); const json = await response.json(); data = json.result.payload; if (data.status) { break; } await new Promise((r) => setTimeout(r, 10000)); } console.log(data.string); // Full outline as text ``` ### Listing Briefs Retrieve a paginated list of all your content briefs: ```python Python theme={null} response = requests.get( f"{BASE_URL}/api/content-brief/orders/", headers=HEADERS, params={"page": 1, "page_size": 20}, ) data = response.json()["result"]["payload"] print(f"Total briefs: {data['total_orders']}") for brief in data["orders"]: print(f" {brief['keyword']} — {brief['status']}") ``` ```javascript JavaScript theme={null} const response = await fetch(`${BASE_URL}/api/content-brief/orders/?page=1&page_size=20`, { headers: { "X-API-Key": API_KEY }, }); const json = await response.json(); const data = json.result.payload; console.log(`Total briefs: ${data.total_orders}`); for (const brief of data.orders) { console.log(` ${brief.keyword} — ${brief.status}`); } ``` ### Complete Example End-to-end: create a content brief, wait for results, then generate an AI outline. ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} def create_brief(keyword, language="en", location="United States"): """Create a content brief order.""" response = requests.post( f"{BASE_URL}/api/content-brief/order/", headers=HEADERS, json={"keyword": keyword, "language": language, "location": location, "folder_id": ""}, ) response.raise_for_status() return response.json()["result"]["payload"] def wait_for_brief(order_id): """Poll until the content brief is ready.""" while True: response = requests.get( f"{BASE_URL}/api/content-brief/order/", headers=HEADERS, params={"id": order_id}, ) data = response.json()["result"]["payload"] if data["status"]: return data print("Brief processing...") time.sleep(15) def generate_outline(order_id, context=""): """Trigger AI outline generation and wait for results.""" response = requests.post( f"{BASE_URL}/api/content-brief/order/{order_id}/outline/", headers=HEADERS, json={"additional_context": context}, ) auto_id = response.json()["result"]["payload"]["auto_generate_order_id"] while True: response = requests.get( f"{BASE_URL}/api/content-brief/order/{order_id}/outline/", headers=HEADERS, params={"auto_generate_order_id": auto_id}, ) data = response.json()["result"]["payload"] if data["status"]: return data time.sleep(10) # --- Run --- result = create_brief("best project management tools") order_id = result["id"] print(f"Brief created: {order_id} (cost: {result['cost']} credits)") brief = wait_for_brief(order_id) print(f"Target: {brief['word_count_min']}–{brief['word_count_max']} words") print(f"Title suggestions: {brief['brief_title_suggests']}") outline = generate_outline(order_id, context="Focus on remote teams") print(f"\nAI Outline:\n{outline['string']}") ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; async function createBrief(keyword, language = "en", location = "United States") { const response = await fetch(`${BASE_URL}/api/content-brief/order/`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ keyword, language, location, folder_id: "" }), }); const json = await response.json(); return json.result.payload; } async function waitForBrief(orderId) { while (true) { const response = await fetch(`${BASE_URL}/api/content-brief/order/?id=${orderId}`, { headers: { "X-API-Key": API_KEY }, }); const json = await response.json(); const data = json.result.payload; if (data.status) { return data; } console.log("Brief processing..."); await new Promise((r) => setTimeout(r, 15000)); } } async function generateOutline(orderId, context = "") { const response = await fetch(`${BASE_URL}/api/content-brief/order/${orderId}/outline/`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ additional_context: context }), }); const json = await response.json(); const autoId = json.result.payload.auto_generate_order_id; while (true) { const response = await fetch( `${BASE_URL}/api/content-brief/order/${orderId}/outline/?auto_generate_order_id=${autoId}`, { headers: { "X-API-Key": API_KEY }, } ); const json = await response.json(); const data = json.result.payload; if (data.status) { return data; } await new Promise((r) => setTimeout(r, 10000)); } } // --- Run --- (async () => { const result = await createBrief("best project management tools"); const orderId = result.id; console.log(`Brief created: ${orderId} (cost: ${result.cost} credits)`); const brief = await waitForBrief(orderId); console.log(`Target: ${brief.word_count_min}–${brief.word_count_max} words`); console.log(`Title suggestions: ${brief.brief_title_suggests}`); const outline = await generateOutline(orderId, "Focus on remote teams"); console.log(`\nAI Outline:\n${outline.string}`); })(); ``` # Public API: Keyword Content Source: https://docs.keywordinsights.ai/api/api-use-cases/public-api-keyword-content Extract People Also Ask, Reddit/Quora questions, and AI-generated meta titles and descriptions via the API Extract content research data for any keyword — People Also Ask questions, Reddit and Quora discussions, and AI-generated meta titles and descriptions. Full endpoint reference available on [Swagger](https://api.keywordinsights.ai/apidocs/#/Keyword%20Content). ### Quick Start Create a keyword content order to extract People Also Ask questions: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} payload = { "keyword": "best project management tools", "language": "en", "location": "United States", "content_insights": ["paa"], } response = requests.post( f"{BASE_URL}/api/keyword-content/order/", headers=HEADERS, json=payload, ) data = response.json() print(data) # {"status": true, "order_id": "2879fa0b-...", "cost": 50} ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY, "Content-Type": "application/json" }; const payload = { keyword: "best project management tools", language: "en", location: "United States", content_insights: ["paa"], }; async function createOrder() { const response = await fetch(`${BASE_URL}/api/keyword-content/order/`, { method: "POST", headers: HEADERS, body: JSON.stringify(payload), }); const data = await response.json(); console.log(data); // {"status": true, "order_id": "2879fa0b-...", "cost": 50} } createOrder(); ``` ### Endpoints | Method | Path | Description | | ------ | ------------------------------------------- | ------------------------------ | | POST | `/api/keyword-content/order/` | Create a keyword content order | | GET | `/api/keyword-content/order/?order_id={id}` | Get order status and results | ### Parameters #### Required | Parameter | Type | Description | | ------------------ | --------- | ----------------------------------------------------------------------------- | | `keyword` | string | Target keyword to research | | `language` | string | Language code (e.g. `en`). See `/api/content-brief/languages/` | | `location` | string | Location name (e.g. `United States`). See `/api/keywords-insights/locations/` | | `content_insights` | string\[] | Types of content insights to extract (see below) | #### Optional | Parameter | Type | Default | Description | | --------- | ------ | --------- | --------------------- | | `device` | string | `desktop` | `desktop` or `mobile` | ### Content Insight Types The `content_insights` array controls which data is extracted. You can combine multiple types in a single order: | Insight | Description | Cost | | ------------------- | ------------------------------------------ | ----------- | | `paa` | People Also Ask questions from Google SERP | 50 credits | | `reddit_questions` | Related Reddit discussions and questions | 50 credits | | `quora_questions` | Related Quora questions | 50 credits | | `meta_titles` | AI-generated meta title suggestions | 200 credits | | `meta_descriptions` | AI-generated meta description suggestions | 200 credits | Total cost = sum of selected insights. ### Customizations #### All Content Insights Extract everything in a single order: ```python Python theme={null} payload = { "keyword": "email marketing automation", "language": "en", "location": "United States", "content_insights": [ "paa", "reddit_questions", "quora_questions", "meta_titles", "meta_descriptions", ], } # Cost: 50 + 50 + 50 + 200 + 200 = 550 credits ``` ```javascript JavaScript theme={null} const payload = { keyword: "email marketing automation", language: "en", location: "United States", content_insights: [ "paa", "reddit_questions", "quora_questions", "meta_titles", "meta_descriptions", ], }; // Cost: 50 + 50 + 50 + 200 + 200 = 550 credits ``` #### Questions Only Get user questions from multiple platforms at a lower cost: ```python Python theme={null} payload = { "keyword": "best CRM software", "language": "en", "location": "United States", "content_insights": ["paa", "reddit_questions", "quora_questions"], } # Cost: 50 + 50 + 50 = 150 credits ``` ```javascript JavaScript theme={null} const payload = { keyword: "best CRM software", language: "en", location: "United States", content_insights: ["paa", "reddit_questions", "quora_questions"], }; // Cost: 50 + 50 + 50 = 150 credits ``` ### Retrieving Results Poll until the order status is `"done"`: ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717" while True: response = requests.get( f"{BASE_URL}/api/keyword-content/order/", headers=HEADERS, params={"order_id": order_id}, ) data = response.json()["result"]["payload"] if data["status"] == "done": break print("Processing...") time.sleep(10) results = data["results"] # People Also Ask if "paa" in results: print("People Also Ask:") for q in results["paa"]: print(f" - {q}") # Reddit questions if "reddit_questions" in results: print("\nReddit questions:") for q in results["reddit_questions"]: print(f" - {q}") # AI meta titles if "meta_titles" in results: print("\nSuggested meta titles:") for title in results["meta_titles"]: print(f" - {title}") ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY }; const order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"; async function getResults() { let data; while (true) { const url = new URL(`${BASE_URL}/api/keyword-content/order/`); url.searchParams.append("order_id", order_id); const response = await fetch(url, { headers: HEADERS, }); const json = await response.json(); data = json.result.payload; if (data.status === "done") { break; } console.log("Processing..."); await new Promise((r) => setTimeout(r, 10000)); } const results = data.results; // People Also Ask if (results.paa) { console.log("People Also Ask:"); for (const q of results.paa) { console.log(` - ${q}`); } } // Reddit questions if (results.reddit_questions) { console.log("\nReddit questions:"); for (const q of results.reddit_questions) { console.log(` - ${q}`); } } // AI meta titles if (results.meta_titles) { console.log("\nSuggested meta titles:"); for (const title of results.meta_titles) { console.log(` - ${title}`); } } } getResults(); ``` The results include download links for CSV exports: ```python Python theme={null} # Download CSV files files = results.get("files", {}).get("csv", {}) for insight_type, download_url in files.items(): print(f"{insight_type}: {download_url}") ``` ```javascript JavaScript theme={null} // Download CSV files const files = results.files?.csv || {}; for (const [insight_type, download_url] of Object.entries(files)) { console.log(`${insight_type}: ${download_url}`); } ``` ### Complete Example Extract People Also Ask and Reddit questions, then save results to a file: ```python Python theme={null} import os import time import json import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} def create_content_order(keyword, insights, language="en", location="United States"): """Create a keyword content order.""" response = requests.post( f"{BASE_URL}/api/keyword-content/order/", headers=HEADERS, json={ "keyword": keyword, "language": language, "location": location, "content_insights": insights, }, ) response.raise_for_status() return response.json() def wait_for_results(order_id): """Poll until results are ready.""" while True: response = requests.get( f"{BASE_URL}/api/keyword-content/order/", headers=HEADERS, params={"order_id": order_id}, ) data = response.json()["result"]["payload"] if data["status"] == "done": return data["results"] print("Processing...") time.sleep(10) # --- Run --- result = create_content_order( keyword="best project management tools", insights=["paa", "reddit_questions"], ) order_id = result["order_id"] print(f"Order created: {order_id} (cost: {result['cost']} credits)") results = wait_for_results(order_id) # Save to file with open("content_research.json", "w") as f: json.dump(results, f, indent=2) paa_count = len(results.get("paa", [])) reddit_count = len(results.get("reddit_questions", [])) print(f"Saved {paa_count} PAA questions and {reddit_count} Reddit questions") ``` ```javascript JavaScript theme={null} const fs = require("fs"); const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const HEADERS = { "X-API-Key": API_KEY, "Content-Type": "application/json" }; async function createContentOrder(keyword, insights, language = "en", location = "United States") { const response = await fetch(`${BASE_URL}/api/keyword-content/order/`, { method: "POST", headers: HEADERS, body: JSON.stringify({ keyword, language, location, content_insights: insights, }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } async function waitForResults(order_id) { while (true) { const url = new URL(`${BASE_URL}/api/keyword-content/order/`); url.searchParams.append("order_id", order_id); const response = await fetch(url, { headers: HEADERS, }); const json = await response.json(); const data = json.result.payload; if (data.status === "done") { return data.results; } console.log("Processing..."); await new Promise((r) => setTimeout(r, 10000)); } } // --- Run --- async function run() { const result = await createContentOrder( "best project management tools", ["paa", "reddit_questions"] ); const order_id = result.order_id; console.log(`Order created: ${order_id} (cost: ${result.cost} credits)`); const results = await waitForResults(order_id); // Save to file fs.writeFileSync("content_research.json", JSON.stringify(results, null, 2)); const paa_count = (results.paa || []).length; const reddit_count = (results.reddit_questions || []).length; console.log(`Saved ${paa_count} PAA questions and ${reddit_count} Reddit questions`); } run(); ``` # Public API: Keyword Discovery Source: https://docs.keywordinsights.ai/api/api-use-cases/public-api-keyword-discovery Generate keyword research from a seed keyword and retrieve enriched keyword data via the API Discover new keyword opportunities from a seed keyword — including autocomplete suggestions, related searches, People Also Ask, and search volume metrics. Full endpoint reference available on [Swagger](https://api.keywordinsights.ai/apidocs/#/Keyword%20Discovery). ### Quick Start Create a keyword discovery order from a seed keyword: ```python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} payload = { "seed_keyword": "content marketing strategies", "language": "en", "location": "United States", } response = requests.post( f"{BASE_URL}/api/keyword-discovery/order/", headers=HEADERS, json=payload, ) data = response.json() print(data) # {"status": true, "payload": {"id": "339ca10b-...", "status": "created", ...}} ``` ### Endpoints | Method | Path | Description | | ------ | -------------------------------------------------- | ------------------------------------------------- | | POST | `/api/keyword-discovery/order/` | Create a keyword discovery order | | GET | `/api/keyword-discovery/order/{order_id}/` | Get order details and processing status (polling) | | DELETE | `/api/keyword-discovery/order/{order_id}/` | Delete an order (owner only) | | GET | `/api/keyword-discovery/order/{order_id}/keywords` | Get discovered keywords (paginated, filterable) | | GET | `/api/keyword-discovery/orders/` | List your orders (paginated) | | GET | `/api/keyword-discovery/languages/` | List supported languages | | GET | `/api/keyword-discovery/locations/` | List all supported locations | | GET | `/api/keyword-discovery/locations/{search}/` | Search locations by name (top 10 fuzzy matches) | ### Parameters #### Create Order — Required | Parameter | Type | Description | | -------------- | ------ | ----------------------------------------------------------------------------- | | `seed_keyword` | string | The main keyword to discover related keywords for | | `language` | string | ISO 639-1 language code (e.g. `en`). See `/api/keyword-discovery/languages/` | | `location` | string | Location name (e.g. `United States`). See `/api/keyword-discovery/locations/` | #### Create Order — Optional | Parameter | Type | Description | | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------- | | `folder_id` | string | Dashboard folder ID to organize the order. Also pulls workspace/domain settings if the folder is linked to a domain | ### Polling Order Status Orders are processed asynchronously. Poll the order endpoint until `payload.status` is `done` (or `error`). Each data source reports its own progress in `payload.processing_status`; keyword metrics (volume, CPC, competition) are attached once `processing_status.enrich_status` is `done`. ```python theme={null} import time def wait_for_order(order_id, poll_interval=10, max_wait=300): """Poll until the order finishes processing.""" start = time.time() while time.time() - start < max_wait: response = requests.get( f"{BASE_URL}/api/keyword-discovery/order/{order_id}/", headers=HEADERS, ) response.raise_for_status() payload = response.json()["payload"] status = payload["status"] processing = payload.get("processing_status") or {} print(f"status: {status}, enrichment: {processing.get('enrich_status')}") if status == "error": raise RuntimeError("Order failed to process") if status == "done" and processing.get("enrich_status") == "done": return payload time.sleep(poll_interval) raise TimeoutError(f"Order not ready after {max_wait}s") ``` The order detail response also includes `n_keywords` (total discovered so far), a short `keywords` preview, and `seed_keyword_data` (metrics for the seed keyword itself). | Status | Meaning | | ------------ | ----------------------------------------------------------------------------------- | | `created` | Order accepted, processing not started | | `processing` | Keyword sources are being fetched | | `done` | All sources fetched — check `processing_status.enrich_status` for metric enrichment | | `error` | Processing failed (credits are refunded automatically) | ### Retrieving Keywords Once the order is complete, retrieve discovered keywords with filtering and sorting: ```python theme={null} order_id = "339ca10b-80c1-4248-b8d0-c11377c204c7" response = requests.get( f"{BASE_URL}/api/keyword-discovery/order/{order_id}/keywords", headers=HEADERS, params={ "page_size": 50, "page_number": 1, "sort_by": "volume", "sort_order": "desc", }, ) data = response.json()["payload"] print(f"Total keywords: {data['total_list_length']}") for kw in data["list"]: print(f" {kw['keyword']} — vol: {kw['volume']}, source: {kw['source']}") ``` #### Keywords Query Parameters **Pagination & sorting:** | Parameter | Type | Default | Description | | ------------- | ------- | -------- | --------------------------------------------------------------- | | `page_size` | integer | 10 | Results per page (max 10000) | | `page_number` | integer | 1 | Page number | | `sort_by` | string | `volume` | Sort field: `keyword`, `source`, `volume`, `cpc`, `competition` | | `sort_order` | string | `desc` | `asc` or `desc` | **Source filtering:** | Parameter | Type | Description | | --------- | --------- | ---------------------------------------------------------- | | `source` | string | Filter by single source | | `sources` | string\[] | Filter by multiple sources (pass as repeated query params) | Available sources: `seed_keyword`, `google_autocomplete`, `google_related_searches`, `people_also_ask`, `quora`, `reddit`, `google_search_console`, `generated_keywords` **Keyword filtering:** | Parameter | Type | Description | | ------------------- | --------- | -------------------------------------------- | | `keywords_included` | string\[] | Only include keywords containing these terms | | `keywords_excluded` | string\[] | Exclude keywords containing these terms | | `include_hidden` | boolean | Include hidden keywords (default `false`) | **Metric filters:** | Parameter | Type | Description | | ------------------------------------- | ------- | ---------------------------------------------------- | | `volume_from` / `volume_to` | integer | Search volume range | | `cpc_from` / `cpc_to` | float | CPC range | | `competition_from` / `competition_to` | float | Competition value range | | `competition_cat` | string | Competition category: `all`, `low`, `medium`, `high` | | `ctr_from` / `ctr_to` | float | CTR range (GSC data) | | `clicks_from` / `clicks_to` | integer | Clicks range (GSC data) | | `position_from` / `position_to` | float | SERP position range (GSC data) | | `impressions_from` / `impressions_to` | integer | Impressions range (GSC data) | ### Managing Orders #### Listing Your Orders ```python theme={null} response = requests.get( f"{BASE_URL}/api/keyword-discovery/orders/", headers=HEADERS, params={ "sort_by": "created_date", "sort_order": "desc", "search_query": "marketing", # optional: filter by seed keyword "page_size": 10, }, ) data = response.json()["payload"] for order in data["list"]: print(f"{order['seed_keyword']} — {order['status']} ({order['n_keywords']} keywords)") ``` | Parameter | Type | Default | Description | | -------------- | ------- | ------- | -------------------------------- | | `sort_by` | string | — | `seed_keyword` or `created_date` | | `sort_order` | string | `asc` | `asc` or `desc` | | `search_query` | string | — | Filter orders by seed keyword | | `page_size` | integer | 10 | Results per page | | `page_number` | integer | 1 | Page number | #### Deleting an Order Only the order owner can delete an order: ```python theme={null} response = requests.delete( f"{BASE_URL}/api/keyword-discovery/order/{order_id}/", headers=HEADERS, ) # {"status": true, "payload": {"status": "deleted"}, "error": null} ``` ### Languages & Locations Look up valid values for the create-order `language` and `location` fields: ```python theme={null} # All supported languages: [{"name": "English", "code": "en"}, ...] languages = requests.get( f"{BASE_URL}/api/keyword-discovery/languages/", headers=HEADERS ).json()["payload"] # Search locations by name (top 10 fuzzy matches) locations = requests.get( f"{BASE_URL}/api/keyword-discovery/locations/united/", headers=HEADERS ).json()["payload"] # [{"name": "United States", "code": "us"}, {"name": "United Kingdom", "code": "gb"}, ...] ``` The full `/api/keyword-discovery/locations/` list is large — prefer the search variant when you know the location name. ### Customizations #### Filtering by Source Narrow results to specific keyword sources: ```python theme={null} # Only autocomplete and People Also Ask keywords response = requests.get( f"{BASE_URL}/api/keyword-discovery/order/{order_id}/keywords", headers=HEADERS, params={ "sources": ["google_autocomplete", "people_also_ask"], "sort_by": "volume", "sort_order": "desc", }, ) ``` #### Filtering by Metrics Find high-volume, low-competition keywords: ```python theme={null} response = requests.get( f"{BASE_URL}/api/keyword-discovery/order/{order_id}/keywords", headers=HEADERS, params={ "volume_from": 1000, "competition_cat": "low", "sort_by": "volume", "sort_order": "desc", }, ) ``` ### Complete Example Create a keyword discovery order, poll until it completes, retrieve the top keywords by volume, and clean up: ```python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} def create_discovery(seed_keyword, language="en", location="United States"): """Create a keyword discovery order.""" response = requests.post( f"{BASE_URL}/api/keyword-discovery/order/", headers=HEADERS, json={ "seed_keyword": seed_keyword, "language": language, "location": location, }, ) response.raise_for_status() return response.json()["payload"] def wait_for_order(order_id, poll_interval=10, max_wait=300): """Poll the order endpoint until processing completes.""" start = time.time() while time.time() - start < max_wait: response = requests.get( f"{BASE_URL}/api/keyword-discovery/order/{order_id}/", headers=HEADERS, ) response.raise_for_status() payload = response.json()["payload"] processing = payload.get("processing_status") or {} if payload["status"] == "error": raise RuntimeError("Order failed to process") if payload["status"] == "done" and processing.get("enrich_status") == "done": return payload time.sleep(poll_interval) raise TimeoutError(f"Order not ready after {max_wait}s") def get_keywords(order_id, page_size=50, min_volume=0): """Fetch discovered keywords with optional volume filter.""" response = requests.get( f"{BASE_URL}/api/keyword-discovery/order/{order_id}/keywords", headers=HEADERS, params={ "page_size": page_size, "page_number": 1, "sort_by": "volume", "sort_order": "desc", "volume_from": min_volume, }, ) response.raise_for_status() return response.json()["payload"] # --- Run --- result = create_discovery("project management software") order_id = result["id"] print(f"Order created: {order_id}") order = wait_for_order(order_id) print(f"Processing complete: {order['n_keywords']} keywords discovered") data = get_keywords(order_id, page_size=20, min_volume=500) print(f"\nTop {len(data['list'])} keywords (of {data['total_list_length']} total):\n") for kw in data["list"]: print(f" {kw['keyword']:50s} vol: {kw['volume']:>6} source: {kw['source']}") # Optional cleanup requests.delete(f"{BASE_URL}/api/keyword-discovery/order/{order_id}/", headers=HEADERS) ``` # Public API: Writer Agent Source: https://docs.keywordinsights.ai/api/api-use-cases/public-api-writer-agent Generate AI-powered articles with outlines, plans, and full content via the API Generate full AI-written articles from a keyword — including research, outline, and polished content. Full endpoint reference available on [Swagger](https://api.keywordinsights.ai/apidocs/#/Writer%20Agent). ### Quick Start Create a writer agent order to generate an article: ```python Python theme={null} import os import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} payload = { "keyword": "content marketing strategies", "language_code": "en", "location_name": "United States", "folder_id": "", } response = requests.post( f"{BASE_URL}/api/writer-agent/order/", headers=HEADERS, json=payload, ) data = response.json() print(data) # {"status": true, "payload": {"id": "550e8400-...", "keyword": "content marketing strategies", ...}} ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const payload = { keyword: "content marketing strategies", language_code: "en", location_name: "United States", folder_id: "", }; const response = await fetch(`${BASE_URL}/api/writer-agent/order/`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify(payload), }); const data = await response.json(); console.log(data); // {"status": true, "payload": {"id": "550e8400-...", "keyword": "content marketing strategies", ...}} ``` ### Endpoints | Method | Path | Description | | ------ | ---------------------------------- | ------------------------------------------------- | | POST | `/api/writer-agent/order/` | Create a writer agent order | | GET | `/api/writer-agent/order/?id={id}` | Get order status and generated article | | GET | `/api/writer-agent/options/` | Get available options (POV, content types, tones) | ### Parameters #### Required | Parameter | Type | Description | | --------------- | ------ | --------------------------------------------------------------------------------------------- | | `keyword` | string | The main keyword or topic for the article | | `language_code` | string | ISO 639-1 language code (e.g. `en`) | | `location_name` | string | Location name (e.g. `United States`). See `/api/keywords-insights/locations/` | | `folder_id` | string | Dashboard folder ID to organize the order. Retrieve from the browser URL in the KWI dashboard | #### Optional | Parameter | Type | Default | Description | | --------------------- | ------ | --------------------- | ----------------------------------------------------------------------------- | | `point_of_view` | string | `First person (I/We)` | `First person (I/We)`, `Second Person (You)`, `Third Person (He/She/They/It)` | | `content_type` | string | `article` | `article` or `landing_page` | | `additional_insights` | string | — | Extra context or instructions for the AI (e.g. "Focus on B2B companies") | ### Customizations #### Point of View Control the writing perspective: ```python Python theme={null} payload = { "keyword": "best CRM software", "language_code": "en", "location_name": "United States", "point_of_view": "Second Person (You)", # Speaks directly to the reader } ``` ```javascript JavaScript theme={null} const payload = { keyword: "best CRM software", language_code: "en", location_name: "United States", point_of_view: "Second Person (You)", // Speaks directly to the reader }; ``` #### Content Type Choose between article-style content or landing page copy: ```python Python theme={null} payload = { "keyword": "project management tool", "language_code": "en", "location_name": "United States", "content_type": "landing_page", } ``` ```javascript JavaScript theme={null} const payload = { keyword: "project management tool", language_code: "en", location_name: "United States", content_type: "landing_page", }; ``` #### Additional Context Steer the AI with specific instructions: ```python Python theme={null} payload = { "keyword": "email marketing tips", "language_code": "en", "location_name": "United States", "additional_insights": "Focus on e-commerce businesses. Include real-world case studies.", } ``` ```javascript JavaScript theme={null} const payload = { keyword: "email marketing tips", language_code: "en", location_name: "United States", additional_insights: "Focus on e-commerce businesses. Include real-world case studies.", }; ``` ### Getting Available Options Retrieve all available options before creating an order: ```python Python theme={null} response = requests.get( f"{BASE_URL}/api/writer-agent/options/", headers=HEADERS, ) options = response.json()["result"]["payload"] print("Points of view:", options["points_of_view"]) print("Content types:", options["content_types"]) print("Tones of voice:", [t["name"] for t in options["tones_of_voice"]]) ``` ```javascript JavaScript theme={null} const response = await fetch(`${BASE_URL}/api/writer-agent/options/`, { headers: { "X-API-Key": API_KEY }, }); const json = await response.json(); const options = json.result.payload; console.log("Points of view:", options.points_of_view); console.log("Content types:", options.content_types); console.log("Tones of voice:", options.tones_of_voice.map((t) => t.name)); ``` ### Retrieving Results Writer agent orders process through multiple stages (outline, plan, article). Poll until the article is generated: ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} order_id = "550e8400-e29b-41d4-a716-446655440000" while True: response = requests.get( f"{BASE_URL}/api/writer-agent/order/", headers=HEADERS, params={"id": order_id}, ) data = response.json()["result"]["payload"] status = data.get("processing_status", {}) print(f"Status: {data.get('status', 'processing')}") if data.get("generated_article"): break time.sleep(30) print(data["generated_article"]) ``` ```javascript JavaScript theme={null} const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; const order_id = "550e8400-e29b-41d4-a716-446655440000"; let data; while (true) { const response = await fetch(`${BASE_URL}/api/writer-agent/order/?id=${order_id}`, { headers: { "X-API-Key": API_KEY }, }); const json = await response.json(); data = json.result.payload; console.log(`Status: ${data.status || "processing"}`); if (data.generated_article) { break; } await new Promise((r) => setTimeout(r, 30000)); } console.log(data.generated_article); ``` ### Complete Example End-to-end: create a writer agent order, wait for the article, and save it. ```python Python theme={null} import os import time import requests API_KEY = os.environ["KWI_API_KEY"] BASE_URL = "https://api.keywordinsights.ai" HEADERS = {"X-API-Key": API_KEY} def create_article(keyword, language="en", location="United States", **kwargs): """Create a writer agent order.""" payload = { "keyword": keyword, "language_code": language, "location_name": location, "folder_id": "", **kwargs, } response = requests.post( f"{BASE_URL}/api/writer-agent/order/", headers=HEADERS, json=payload, ) response.raise_for_status() return response.json()["result"]["payload"] def wait_for_article(order_id): """Poll until the article is generated.""" while True: response = requests.get( f"{BASE_URL}/api/writer-agent/order/", headers=HEADERS, params={"id": order_id}, ) data = response.json()["result"]["payload"] if data.get("generated_article"): return data print(f"Processing... status: {data.get('status', 'unknown')}") time.sleep(30) # --- Run --- result = create_article( keyword="best project management tools for startups", point_of_view="Second Person (You)", content_type="article", additional_insights="Include comparisons and pricing information", ) order_id = result["id"] print(f"Order created: {order_id}") article = wait_for_article(order_id) # Save the article with open("article.md", "w") as f: f.write(article["generated_article"]) print(f"Article saved ({len(article['generated_article'])} characters)") ``` ```javascript JavaScript theme={null} const fs = require("fs"); const API_KEY = process.env.KWI_API_KEY; const BASE_URL = "https://api.keywordinsights.ai"; async function createArticle(keyword, language = "en", location = "United States", options = {}) { const payload = { keyword, language_code: language, location_name: location, folder_id: "", ...options, }; const response = await fetch(`${BASE_URL}/api/writer-agent/order/`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify(payload), }); const json = await response.json(); return json.result.payload; } async function waitForArticle(orderId) { while (true) { const response = await fetch(`${BASE_URL}/api/writer-agent/order/?id=${orderId}`, { headers: { "X-API-Key": API_KEY }, }); const json = await response.json(); const data = json.result.payload; if (data.generated_article) { return data; } console.log(`Processing... status: ${data.status || "unknown"}`); await new Promise((r) => setTimeout(r, 30000)); } } // --- Run --- (async () => { const result = await createArticle("best project management tools for startups", "en", "United States", { point_of_view: "Second Person (You)", content_type: "article", additional_insights: "Include comparisons and pricing information", }); const orderId = result.id; console.log(`Order created: ${orderId}`); const article = await waitForArticle(orderId); // Save the article fs.writeFileSync("article.md", article.generated_article); console.log(`Article saved (${article.generated_article.length} characters)`); })(); ``` # How to use the Public API? Source: https://docs.keywordinsights.ai/api/how-to-use-the-public-api Our public API is documented with Swagger and can be accessed at [https://api.keywordinsights.ai/apidocs](https://api.keywordinsights.ai/apidocs/). **Bearer token authentication is deprecated.** We recommend switching to API keys, which are simpler to set up, don't expire, and don't require you to handle token refresh. See [API Key Authentication](/api/api-key-authentication) to get started. Bearer tokens will continue to work for now, but may be removed in a future update. ### Prerequisites to use the public API You need to have a valid **Keyword Insights** subscription in order to use the public API. Before using the public API, you must acquire a **Bearer token** for authentication so that our servers can verify who you are and that you have permission to use the API. ### How to use the interactive public API documentation To use the Swagger UI interactively, you must first be authenticated.\ Follow the instructions below to generate a **Bearer token**. Once you are authenticated, you can **Try it out** on any endpoint by providing your Bearer token and the required parameters. ### Authenticate and retrieve a Bearer token (email and password) This approach is for users who signed up with an email and password (not via Google Sign In).\ If you signed up with **Create with Google**, use the Google Sign In approach in the next section instead:\ [How to authenticate with the public API and retrieve a bearer token? (Google Sign In Approach)](how-to-use-the-public-api.mdx#how-to-authenticate-with-the-public-api-and-retrieve-a-bearer-token-google-sign-in-approach) 1. Open the [API documentation](https://api.keywordinsights.ai/apidocs/), scroll to the bottom of the page, and locate the **Authentication** section. Authentication section in Keyword Insights Swagger public API Alternatively, you can go directly to the authentication endpoint using this link:\ [Authentication → /authentication/login](https://api.keywordinsights.ai/apidocs/#/Authentication/post_authentication_login_) You should now see the authentication request details, including a **Try it out** button. Click the Try it out button to get started using the Keyword Insights public API 2. After clicking **Try it out**, the request body field becomes editable. 1. Enter your valid email address and password, exactly as you would on `app.keywordinsights.ai`. 2. Click the large blue **Execute** button below the text field. Enter email and password, then click Execute If your email and password are correct, you should see a **200** response code and a JSON response object containing a `result` object. 3. In the response, copy the value of the `access_token`.\ In the example below, the token starts with `eyJ0....` and ends with `...Mokg`. You will need this token in the next step. Viewing the access token upon a successful authentication request 4. In a text editor of your choice, paste the copied access token and prepend the word `Bearer` (with a capital **B**), separated by a single space.\ Your complete string should look similar to: ``` Bearer eyJ0e..rest_of_the_token...4AnRMokg ``` This full string (including the word `Bearer`) is what you will use as the **Authorization** header for any authenticated API request.\ Some endpoints also require additional arguments such as `order_id`, which you can usually find in the URL of the respective page in the application. 5. Validate your Bearer token using the steps in the section\ [Validate your Bearer Token](how-to-use-the-public-api.mdx#validate-your-bearer-token). ### Authenticate and retrieve a Bearer token (Google Sign In) Use this approach if you signed up using **Create with Google** instead of creating a password during sign up. If you signed up manually with an email and password, use the previous section instead:\ [How to authenticate with the public API and retrieve a bearer token? (Email and password approach)](how-to-use-the-public-api.mdx#how-to-authenticate-with-the-public-api-and-retrieve-a-bearer-token-email-and-password-approach) 1. In your browser of choice (Chrome in this example), open a new tab and open the **Network** tab in the developer tools. Opening the developer tools in Google Chrome Opening the Network tab in the Chrome developer tools 2. In the address bar, navigate to [https://app.keywordinsights.ai](https://app.keywordinsights.ai/).\ Before logging in, click the **clear** button in the Network tab so that you can easily see only the upcoming requests. Clearing the Network tab to more easily inspect the next requests 3. Click **LOG IN WITH GOOGLE** and follow the prompts in the pop-up window as you normally would when signing in to the application. 4. After you are logged in, go back to the Network tab and locate a successful request (for example, the `/user` request – but any authenticated request will work). * Select the request. * In the right-hand panel, make sure the **Headers** tab is selected. * Scroll down until you find the **Authorization** header. * Copy the entire value of the Authorization header, including the word `Bearer`. Copying the Bearer token from an authenticated request in the Network tab 5. Validate your Bearer token using the steps in\ [Validate your Bearer Token](how-to-use-the-public-api.mdx#validate-your-bearer-token). ### Validate your Bearer Token To confirm that your **Bearer token** works correctly, you can call the **User** endpoint in Swagger. 1. In the Swagger UI, navigate to the **User** endpoint and click **Try it out** to make the input fields editable. 2. Paste the full Bearer token string (including the word `Bearer`) into the appropriate field. 3. Click the large blue **Execute** button. Entering the Bearer token to validate it via the User endpoint Executing the request to validate the Bearer token 4. If the Bearer token is valid, you should see a **200** response code and your user object in the response body. Successful response when validating the Bearer token **Congratulations! You have successfully authenticated with our public API. You can now call any endpoint documented in the API reference.**
How much is the API and how is it calculated? The API is available for **Professional** and **Premium** subscriptions.\ For clustering, we provide a calculation endpoint at\ [https://dev.api.keywordinsights.ai/apidocs](https://dev.api.keywordinsights.ai/apidocs/#/)\ that tells you how many credits are required based on your settings.\ The API itself has **no additional pricing** beyond your subscription and credit usage.
# N8N - Content Brief Source: https://docs.keywordinsights.ai/api/n8n/n8n-content-brief Create a basic N8N Flow for Content Briefs ## Step 1 - Authentication via API key * Use an HTTP Request node and select the following options: * Authentication: `Generic Credential Type` * Generic Auth Type: `Header Auth` * Header Auth: `Header Auth account` Authentication section in Mintlify * Then click the `Edit Pencil icon` to open up the detail model to enter your API key: * Then in the modal, add X-API-KEY: `kwi_sk_aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890abc` Authentication section in Mintlify ### Alternatively you can authenticate via username and password * Use an HTTP Request node * URL: `Keywordinsightsapi.keywordinsights.ai//authentication/login` * Method: `POST` * Body -> JSON: `{ "email": "", "password": ""}` Authentication section in Mintlify ## Step 2 - Creating a Content Brief Order * Ensure the output of the Auth node shows in the second node Authentication section in Mintlify * Paste the following json into the body, * `folder_id`: optional if you want the project placed in a specific folder * `secondary_keywords`: optional to provide more detail * `keywords`: pass up to 25 keywords, in a comma separated list within the array brackets `[]` ``` { "keywords": ["how to become a model"], "language": "en", "location": "United States", "title_ai_order_id": "", "title": "", "folder_id": "", "secondary_keywords": "" } ``` Body payload for Content Brief Orders # N8N - Keyword Discovery Source: https://docs.keywordinsights.ai/api/n8n/n8n-keyword-discovery Create a basic N8N Flow for Keyword Discovery Orders ## Step 1 - Authentication via API key * Use an HTTP Request node and select the following options: * Authentication: `Generic Credential Type` * Generic Auth Type: `Header Auth` * Header Auth: `Header Auth account` Authentication section in Mintlify * Then click the `Edit Pencil icon` to open up the detail model to enter your API key: * Then in the modal, add X-API-KEY: `kwi_sk_aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890abc` Authentication section in Mintlify ### Alternatively you can authenticate via username and password * Use an HTTP Request node * URL: `Keywordinsightsapi.keywordinsights.ai/authentication/login` * Method: `POST` * Body -> JSON: `{ "email": "", "password": ""}` Authentication section in Mintlify ## Step 2 - Creating a Keyword Discovery Order * Ensure the output of the Auth node shows in the second node if you are using the legacy username and sandbox Authentication section in Mintlify * Paste the following json into the body, * `folder_id`: optional if you want the project placed in a specific folder ``` { "folder_id": "", "language": "en", "location": "United Kingdom", "seed_keyword": "how to become a model in ukraine" } ``` Body payload for keyword discovery # N8N Workflow - Content Brief Source: https://docs.keywordinsights.ai/api/n8n/n8n-workflow-content-briefs Entire Workflow for N8N Content Briefs N8N Workflow - Content Brief ``` { "name": "Content Brief", "nodes": [ { "parameters": {}, "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ 128, 0 ], "id": "6f96c731-5a5f-4772-a9f8-5d59455913fe", "name": "When clicking ‘Execute workflow’" }, { "parameters": { "method": "POST", "url": "https://api.keywordinsights.ai/authentication/login/", "sendBody": true, "specifyBody": "json", "jsonBody": "{\n \"email\": \"premium@keywordinsights.ai\",\n \"password\": \"gl&Ai&dD3hrHK69sXrDe\"\n}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.3, "position": [ 416, 0 ], "id": "9aea1bcc-a31d-49f1-96c1-2abd12b75692", "name": "Get JWT token" }, { "parameters": { "method": "POST", "url": "https://api.keywordinsights.ai/api/content-brief/order/", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "=Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTc2OTYxMjU3NiwianRpIjoiMDhlZDRjZDctMzkzNi00OTdhLTkyMjgtY2U3MTQ2OTA4ZWMxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6IjI2ODQ3ZDc1LTZiYjItNDAxZS1iZTk0LTA1NDczZGRmYmUyMiIsIm5iZiI6MTc2OTYxMjU3NiwiZXhwIjoxNzcyMjA0NTc2LCJhZG1pbl9hY2Nlc3MiOmZhbHNlLCJwdWJsaWNfYXBpX2FjY2VzcyI6dHJ1ZSwic2hlZXRzX2FkZG9uX2FwaV9hY2Nlc3MiOnRydWUsImlzX2d1ZXN0X3VzZXIiOmZhbHNlfQ.6-Fn0ZkbFb1G2wQlwmn3C14pTyNdVlplLp-ITtfs1ww" }, { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "application/json", "body": "{\"keyword\":\"best electric cars 2024\",\"language\":\"en\",\"location\":\"Canada\"}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.3, "position": [ 752, 0 ], "id": "7506ebff-2750-4238-ad83-42622f0cfa56", "name": "HTTP Request1" }, { "parameters": { "url": "https://api.keywordinsights.ai/api/content-brief/order/", "sendQuery": true, "specifyQuery": "json", "jsonQuery": "={\"id\": \"{{ $('HTTP Request1').item.json.payload.id }}\"}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "=Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTc2OTYxMjU3NiwianRpIjoiMDhlZDRjZDctMzkzNi00OTdhLTkyMjgtY2U3MTQ2OTA4ZWMxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6IjI2ODQ3ZDc1LTZiYjItNDAxZS1iZTk0LTA1NDczZGRmYmUyMiIsIm5iZiI6MTc2OTYxMjU3NiwiZXhwIjoxNzcyMjA0NTc2LCJhZG1pbl9hY2Nlc3MiOmZhbHNlLCJwdWJsaWNfYXBpX2FjY2VzcyI6dHJ1ZSwic2hlZXRzX2FkZG9uX2FwaV9hY2Nlc3MiOnRydWUsImlzX2d1ZXN0X3VzZXIiOmZhbHNlfQ.6-Fn0ZkbFb1G2wQlwmn3C14pTyNdVlplLp-ITtfs1ww" } ] }, "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.3, "position": [ 1328, 0 ], "id": "7361c69f-4099-4066-947b-dc46da124b4e", "name": "Pooling" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "bed320d4-f909-4869-a332-b3fe91a44b8d", "leftValue": "={{ $json.payload.status }}", "rightValue": true, "operator": { "type": "boolean", "operation": "notEquals" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 1584, 0 ], "id": "e399b13d-5c12-42b4-9603-b74d8bcd4df5", "name": "If" }, { "parameters": {}, "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ 1904, 16 ], "id": "a09f8643-8f97-4e7a-99b6-75efeaa67bad", "name": "No Operation, do nothing" }, { "parameters": { "content": "## Pooling for results", "height": 368, "width": 736 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 1024, -176 ], "id": "72a2916b-7f9b-4135-b329-cfbc05bd88c3", "name": "Sticky Note" }, { "parameters": { "amount": 30 }, "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ 1104, 0 ], "id": "7553bbc8-f8fc-4451-8bdb-08faa6abe452", "name": "Wait", "webhookId": "5e9b3a86-86b2-492d-8f34-5b60165ad985" }, { "parameters": { "content": "## Authentication\n\nEnsure to use your keyword insights email/password here, and you have public api access", "height": 336 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 352, -160 ], "id": "86888612-415b-459d-8afc-61e3d1cf1533", "name": "Sticky Note1" }, { "parameters": { "content": "## Create order\nEnsure to customise order configuration here, like keyword, location and language. And folder id. Referr to API docs for more details", "height": 368 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 688, -176 ], "id": "6ac8873f-5aac-4b6d-8187-1ec8ad88809d", "name": "Sticky Note2" } ], "pinData": {}, "connections": { "When clicking ‘Execute workflow’": { "main": [ [ { "node": "Get JWT token", "type": "main", "index": 0 } ] ] }, "Get JWT token": { "main": [ [ { "node": "HTTP Request1", "type": "main", "index": 0 } ] ] }, "HTTP Request1": { "main": [ [ { "node": "Wait", "type": "main", "index": 0 } ] ] }, "Pooling": { "main": [ [ { "node": "If", "type": "main", "index": 0 } ] ] }, "If": { "main": [ [ { "node": "Wait", "type": "main", "index": 0 } ], [ { "node": "No Operation, do nothing", "type": "main", "index": 0 } ] ] }, "Wait": { "main": [ [ { "node": "Pooling", "type": "main", "index": 0 } ] ] } }, "active": false, "settings": { "executionOrder": "v1", "availableInMCP": false }, "versionId": "a4379261-4455-430b-bb04-f8a000744f56", "meta": { "templateCredsSetupCompleted": true, "instanceId": "88ecfcf41c3d06aaa7328ad79aa95ab2c36706c0c1f7a49d83a2dd38d353d849" }, "id": "QisT-dDgt4Rt5aJBF5xqL", "tags": [] } ``` # N8N - Writer Assistant Source: https://docs.keywordinsights.ai/api/n8n/n8n-writer-assistant Create a basic N8N Flow for Writer Assistant Orders ## Step 1 - Authentication via API key * Use an HTTP Request node and select the following options: * Authentication: `Generic Credential Type` * Generic Auth Type: `Header Auth` * Header Auth: `Header Auth account` Authentication section in Mintlify * Then click the `Edit Pencil icon` to open up the detail model to enter your API key: * Then in the modal, add X-API-KEY: `kwi_sk_aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890abc` Authentication section in Mintlify ### Alternatively you can authenticate via username and password * Use an HTTP Request node * URL: `Keywordinsightsapi.keywordinsights.ai/authentication/login` * Method: `POST` * Body -> JSON: `{ "email": "", "password": ""}` Authentication section in Mintlify ## Step 2 - Creating a Writer Assistant Order * Ensure the output of the Auth node shows in the second node if you are using the legacy username and sandbox Authentication section in Mintlify * Paste the following json into the body, * `folder_id`: optional if you want the project placed in a specific folder * `secondary_keywords`: optional to provide more detail * `workflow_type`: NEW | OPTIMIZE | CONTENT\_BRIEF * `keywords`: pass up to 25 keywords, in a comma separated list within the array brackets `[]` ### Workflow Type: NEW ``` { "folder_id": "", "keyword": "how to become a model", "language": "en", "location": "United States", "secondary_keywords": [], "workflow_type": "NEW" } ``` ### Workflow Type: CONTENT\_BRIEF * `content_brief_order_id`: equired parameter of an existing content brief from which you plan to create a Writer Assistant Order ``` { "folder_id": "", "workflow_type": "CONTENT_BRIEF", "content_brief_order_id": "str" } ``` ### Workflow Type: OPTIMIZE ``` { "folder_id": "", "keyword": "news", "language": "en", "location": "United States", "workflow_type": "OPTIMIZE", "content_url": "https://www.bbc.com/...." } ``` Body payload for Writer Assistant Orders # Data storage and limits Source: https://docs.keywordinsights.ai/data-retention/data-storage-and-limits ### What happens to my data if I cancel my subscription? If you cancel your subscription, we will delete your data at the end-of-billing date. All of your data is **permanently deleted**, and we will never be able to recover the data for you. Please make sure to export and back up your data before you cancel. Tip: To avoid losing your data, consider pausing your subscription instead of canceling it. You can pause for up to 3 months, twice per year, during which we’ll securely store your data, settings, and other account details. You can unpause at any time to resume access. ### What happens to my Pay-as-you-go clustering projects? Your Pay-as-you-go clustering projects will be automatically deleted after 30 days. Please make sure to export your projects within 30 days of running your order. We will send you several emails to remind you to export and back up your projects. ### How do I check the project deletion deadlines? We will give you two warnings during your cancellation. We will display a banner with the dates within the application. You can also see the full details on your subscription page. Please be advised that failure to act on your part, resulting in the deletion of your files, will render us unable to recover them. In accordance with our terms and conditions and GDPR compliance, we bear no liability for any data loss. # Exporting Data Source: https://docs.keywordinsights.ai/faqs/exporting-data As clustering reports expanded from 10 to over 40 data columns, the previous export process occasionally hit Google Sheets row limits and struggled to handle larger reports, especially those containing pivot tables. Many users also requested the ability to download raw CSV files for easier use in custom workflows, automations, and scripts. The new export feature addresses all of these issues. You can now export large datasets smoothly to either Google Sheets or CSV without limitations. The process is significantly faster, more stable, and designed to scale with your data needs. Now you will see 2 simple export options. **1. Export Full Project** – Export all project data, including pivot tables, with or without applied filters. You can choose to download it as a Google Sheet or Excel file. **2. Export Raw Data (Non-Pivoted)** – Export all project data without pivot tables, with or without applied filters. Selecting this option enables the CSV export button, allowing you to download the complete dataset in raw CSV format directly from our servers. # Frequently Asked Questions Source: https://docs.keywordinsights.ai/faqs/frequently-asked-questions
How do I tell when my credits are added? For monthly/yearly subscribers, new clustering credits will be added at the end of their billing cycle. Click the User icon to see when the credits are refreshed.
What happens to my credits if I cancel my subscription? After your subscription is cancelled, your monthly/annual credits will be available until the end At the end of your billing cycle, all credits including monthly, top-up, and pay-as-you-go credits will be reset and permanently deleted.
Do unused credits roll over every month? No, credits do not roll-over. Any unused credits will be reset at the end of the billing cycle.
Can I try it before I buy it? Yes, you can try our \$1 trial for 7 days. It comes with 600 universal credits.
What happens after my \$1 trial expires? Your \$1 trial will expire in 7 days. After this, your credits will be reset and you will be restricted from using the tool. You will not be charged anything. (You can still buy credits via Pay as you go for using the clustering feature)
How do special characters in my language affect the output? Special characters are common in many languages. Such as Norwegian, Thai, Swedish, German etc. If you're using Microsoft Excel or Mac Numbers, Please make sure to export your files with UTF-8 encoding. Else your special characters will not be exported correctly, and Keyword insights will show you an error. When possible, we highly recommend you upload an **excel XLXS** file when dealing with special characters instead CSVs. The CSV encoding can convert special characters into random unreadable text, and it will cause our clustering and hub/spoke algorithm to produce incorrect output results.
Some of my clusters appear really similar - why is this? Please watch this video as it explains this in more detail. [https://snippet.wistia.com/medias/oaeqllw2a4](https://snippet.wistia.com/medias/oaeqllw2a4)
What languages does your keyword Context feature support? We support up to 100+ languages.
What languages are supported by the Topic cluster feature? Here is the list of supported languages. * Afrikaans * Albanian * Arabic * Aragonese * Armenian * Asturian * Azerbaijani * Bashkir * Basque * Bavarian * Belarusian * Bengali * Bishnupriya Manipuri * Bosnian * Breton * Bulgarian * Burmese * Catalan * Cebuano * Chechen * Chinese (Simplified) * Chinese (Traditional) * Chuvash * Croatian * Czech * Danish * Dutch * English * Estonian * Finnish * French * Galician * Georgian * German * Greek * Gujarati * Haitian * Hebrew * Hindi * Hungarian * Icelandic * Ido * Indonesian * Irish * Italian * Japanese * Javanese * Kannada * Kazakh * Kirghiz * Korean * Latin * Latvian * Lithuanian * Lombard * Low Saxon * Luxembourgish * Macedonian * Malagasy * Malay * Malayalam * Marathi * Minangkabau * Nepali * Newar * Norwegian (Bokmal) * Norwegian (Nynorsk) * Occitan * Persian (Farsi) * Piedmontese * Polish * Portuguese * Punjabi * Romanian * Russian * Scots * Serbian * Serbo-Croatian * Sicilian * Slovak * Slovenian * South Azerbaijani * Spanish * Sundanese * Swahili * Swedish * Tagalog * Tajik * Tamil * Tatar * Telugu * Turkish * Ukrainian * Urdu * Uzbek * Vietnamese * Volapük * Waray-Waray * Welsh * West Frisian * Western Punjabi * Yoruba
What's the Topic cluster? What's the Hub/spoke model, and how is it different from clusters? Once you have your keyword clusters, it may be helpful to know how closely related those clusters are to other clusters. By grouping similar clusters together, we create Hubs and Spokes. Using these, you'll be able to produce what we call "hub articles" which link to "spoke articles". In essence, our "hub and spoke" tab will make your content planning easier, allowing you to quickly identify and comprehensively cover a given content topic. **Bonus tip**: Pull through the your keyword rankings and you'll be able to see internal linking opportunities, if you have relevant existing content. In this example, "Hawaii vacation" will form the "Hub" content piece. This could be a category page or a long-form article. Here we are show all the "clusters" in the Spoke column that are contextually related to the hub. So, you can produce content around "Where to stay in Hawaii" or "Planning a honeymoon to Hawaii" and internally link to our main "Hawaii vacation" hub .
What countries does your keyword clustering feature support? Keyword clustering is not language-specific but rather geo/country-specific. We support all the countries in the world. Let's take a look at a few examples. I have an SEO client in France, and I'm targeting users in France. When I prepare my keyword list, I will select all the keywords and search volume data for those keywords. I will choose ***France*** under the country dropdown when I run my order through Keyword Insights. Keyword Insights will then scrape ***google.fr*** and build the clusters based on how keywords are ranking in google.fr. You can choose to upload French keywords, English keywords, or any other language. Regardless of language, Keyword Insights will scrape google.fr for those keywords. As you can see, the language has no influence whatsoever on our clustering process.
What languages are supported by the AI Writer assistant? Here is the list of supported languages. * English * German * Norwegian * Swedish * Danish * Spanish * French * Dutch * Ukranian * Russian * Hebrew
Do you have a public product roadmap? We do not have a public roadmap. However, if you're a paying customer, you will get access to our private SEO Community. We often show our early previews and engage with our customers. You can also make feature requests. You can join the community by going [here](https://community.keywordinsights.ai/).
Do you have live chat support? We provide live chat support for Professional and Premium subscribers. Basic subscribers get access to our email support with 24-48hr response time. Our operating hours are between 9 am - 5 pm GMT.
How is the opportunity score calculated? Most tools see how much volume a keyword gets to create an opportunity score, but they don't take into account two important things: 1. A realistic number of visitors based on volume can be determined by looking at the [CTR study by AWR](https://www.advancedwebranking.com/ctrstudy/). Based on this study, if you rank in position 1, you can expect to get 38% of the clicks, position 2 gets 13%, position 3 gets 8% etc. 2. It doesn't consider where you currently rank (and, therefore, the opportunity). So we calculate the opportunity score by doing the following. If someone ranks in position 1 for a keyword, the opportunity score will be "0", as they already rank for it. However, if they rank in position 2, it will be the difference between the percentage scores of position 1 and position 2. So we have 1 column, "maximum opportunity", which is position 1 (or 38%) multiplied by the search volume. Then another column called "current estimated traffic", which is whatever their current position is, multiplied by the volume. So if we were in position two, it would be 13% multiplied by the volume. The opportunity is then the difference between the two. For example, "cats" has a search volume of 100. We rank in position 3. Maximum opportunity = 38 (38% x 100) Current Estimated traffic = 8 (8% x 100) Opportunity = 30 (30 - 8).
My account is flagged as spam. How can I fix this? Your account has been flagged as spam by Google. Google's system is quite sensitive and can occasionally generate false positives. We apologize for any inconvenience this may have caused. Please reach out to our support team through live chat or email, and we will promptly remove the block. Thank you for your understanding.
# Language Support Source: https://docs.keywordinsights.ai/faqs/language-support You can find details of language support for each feature here. We’re continually adding new languages, so if there’s one you’d like us to support, please contact our support team. The following languages are supported across these features: **Keyword Clustering, SERP Similarity, SERP Analyzer, and Title AI.** * Afrikaans * Akan * Albanian * Amharic * Arabic * Armenian * Azeri * Balinese * Basque * Belarusian * Bengali * Bosnian * Bulgarian * Burmese * Catalan * Cebuano * Chichewa * Chinese (Simplified) * Chinese (Traditional) * Croatian * Czech * Danish * Dutch * English * Español (Latinoamérica) * Estonian * Ewe * Faroese * Farsi * Filipino * Finnish * French * Frisian * Ga * Galician * Ganda * Georgian * German * Greek * Gujarati * Haitian * Hausa * Hebrew * Hebrew (old) * Hindi * Hungarian * Icelandic * IciBemba * Igbo * Indonesian * Irish * Italian * Japanese * Kannada * Kazakh * Khmer * Kinyarwanda * Kirundi * Kongo * Korean * Kreol Seselwa * Kreol morisien * Krio * Kurdish * Kyrgyz * Lao * Latvian * Lingala * Lithuanian * Luo * Macedonian * Malagasy * Malay * Malayalam * Maltese * Maori * Marathi * Mongolian * Montenegro * Nepali * Northern Sotho * Norwegian * Nyankole * Oromo * Pashto * Pidgin * Polish * Portuguese * Portuguese (Brazil) * Portuguese (Portugal) * Punjabi * Quechua * Romanian * Romansh * Russian * Serbian * Serbian (Latin) * Sesotho * Shona * Silozi * Sindhi * Sinhalese * Slovak * Slovenian * Somali * Spanish * Swahili * Swedish * Tajik * Tamil * Telugu * Thai * Tigrinya * Tonga (Tonga Islands) * Tshiluba * Tswana * Tumbuka * Turkish * Turkmen * Ukrainian * Urdu * Uzbek * Vietnamese * Wolof * Xhosa * Yoruba * Zulu The following languages are supported across these features: **Keyword Discovery, Content briefs, Writer assistant, and AI Writer agent.** * Polish * Spanish * Italian * Norwegian * German * French * Danish * English * Dutch * Arabic * Portuguese # Affiliate Program Source: https://docs.keywordinsights.ai/help-and-support/affiliate-program Help others discover the power of Keyword Insights and get rewarded for it. When you refer someone to Keyword Insights using your unique referral link, you’ll earn 20% commission on every sale they make, for life. This is more than a one-time payout. It’s a true passive income stream: every time someone you refer upgrades or renews, you get paid. No limits, no expiration. Whether you’re recommending it to colleagues, clients, or your online audience, it’s a win-win. They get access to one of the most powerful topical authority platforms on the market and you get paid every step of the way. ### How do I join the affiliate program? Signing up is quick and easy. Just head to our affiliate registration page and [create an account](https://www.keywordinsights.ai/affiliate-program/). Once you’re approved, you’ll get instant access to your unique referral link and dashboard. From there, you can start promoting Keyword Insights and track your clicks, conversions, and commissions in real time. If you’re already a user, you can log in with your existing account and activate your affiliate status from the dashboard. ### What platform do you use for your affiliate program? We use [Tolt](https://app.tolt.io/) as our affiliate management program. ### How do i login to my dashboard? Once approved, you can login to the dashboard by going [here](https://affiliates.keywordinsights.ai/login) ### **How to Use The Dashboard?** Go to the login page, add your email address and click "Login" You will get a code sent via email and you can use it to login to your dashboard. ### The Dashboard You can view all your affiliate data in one place. Including referrals, commissions, payouts, and payment settings. From your dashboard, you’ll also be able to choose and manage your preferred payout method. ### What payout methods are available? We support multiple payout options to suit your preference. You can choose to receive your commissions via Wise, PayPal, local bank transfer, or wire transfer. Simply select your preferred method in the payout settings of your affiliate dashboard. ### How to set my payout method? Go to Settings -> Payment method -> Click "Update payment details" Select your preferred payment method. Click "Save" ### When will i get paid? Payouts will happen automatically, 15 days after the end of each month, for the previous month. ### What is the payment threshold? When your affiliate amount reaches \$100 you will be qualified to get a payout. ### What are the Terms & Conditions for using your affiliate program? We’ve updated our Affiliate Terms & Conditions to better reflect how the program works. You can read the new terms [here](https://www.keywordinsights.ai/affiliate-program-terms-and-conditions/) # Changelog Source: https://docs.keywordinsights.ai/help-and-support/changelog You can access our changelog here