# API Quickstart

<div data-full-width="true"><figure><img src="/files/QFq0i4HBcmib5bQDrr9H" alt=""><figcaption><p>Example search for Tesla stock outlook</p></figcaption></figure></div>

Making your first request with the Athena API is straightforward with this script.&#x20;

Athena's News API operates asynchronously — meaning that when you are submitting your query, a job is created (along with a unique job id). You'll then have to poll the get-results endpoint with that job id to see if your job is done.&#x20;

* Submit query and get a query id
* Every x seconds poll the get-results endpoint to see if the query is finished, passing in the query id from the previous step
* If the search is complete, your results will be returned

### Using Async Query Endpoint

First define your `ATHENA_API_KEY`. The `payload` dictionary specifies the query parameters, including a search term ("donald trump"), your api\_key, other configuration details to filter the results (for the sake of simplicity we are choosing to omit extra parameters — see [API Reference](/athena-api/getting-started/api-reference.md) for other options).&#x20;

The initial request is sent via a `POST` call to Athena’s query-async API endpoint. The response contains your query\_id, which is how you will retrieve results from your search with the `/api/v2/get-results` endpoint.

{% tabs %}
{% tab title="Python" %}

```python
import time
import requests

# Constants
ATHENA_API_KEY = "YOUR_API_KEY"
QUERY = "Tesla dealership protests"
KEY_PHRASES = "('tesla takedown')"
start_date = "2025-03-01T15:13:52.466Z"
end_date = "2025-03-20T15:13:52.466Z"
QUERY_URL = "https://app.runathena.com/api/v2/query-async"
RESULTS_URL = "https://app.runathena.com/api/v2/get-results"
HEADERS = {"Content-Type": "application/json"}
ARTICLES_PER_PAGE = 25
POLL_INTERVAL = 1  # seconds to wait between polls

def send_initial_query(query: str, api_key: str) -> str:
    """
    Sends the initial query to the API and returns the query_id.
    """
    payload = {"query": query, "key_phrases":KEY_PHRASES,"api_key": api_key,'toggle_state':'All Articles',"start_date":start_date,"end_date":end_date}
    response = requests.post(QUERY_URL, headers=HEADERS, json=payload)
    response.raise_for_status()  # raise an error for bad responses
    data = response.json()
    return data.get('query_id')

def poll_for_results(query_id: str, api_key: str, interval: int = POLL_INTERVAL) -> dict:
    """
    Polls the API until the query status is not 'PENDING'.
    Returns the final result data.
    """
    payload = {"query_id": query_id, "api_key": api_key}
    response = requests.post(RESULTS_URL, headers=HEADERS, json=payload)
    response.raise_for_status()
    data = response.json()
    
    # Poll until the status changes from 'PENDING'
    while data.get('state') == 'PENDING':
        time.sleep(interval)
        response = requests.post(RESULTS_URL, headers=HEADERS, json=payload)
        response.raise_for_status()
        data = response.json()
    
    return data

def fetch_all_articles(query_id: str, total_results: int, api_key: str) -> list:
    """
    Fetches and aggregates all articles by paginating through the results.
    """
    all_articles = []
    page = 1
    payload = {"query_id": query_id, "api_key": api_key,'toggle_state':'Encoded Articles'}
    
    # Continue fetching while there are articles remaining.
    while (page - 1) * ARTICLES_PER_PAGE < total_results:
        payload['page'] = page
        response = requests.post(RESULTS_URL, headers=HEADERS, json=payload)
        response.raise_for_status()
        data = response.json()
        
        articles = data.get('articles', [])
        all_articles.extend(articles)
        page += 1
    
    return all_articles

def main():
    try:
        # Step 1: Send the initial query and retrieve the query_id.
        query_id = send_initial_query(QUERY, ATHENA_API_KEY)
        if not query_id:
            print("Failed to retrieve query ID.")
            return
        
        # Step 2: Poll the API until the query is processed.
        result_data = poll_for_results(query_id, ATHENA_API_KEY)
        
        #print(result_data)
        #print(result_data.keys())
        # Check if the query was successful before pagination.
        if result_data.get('state') != 'SUCCESS':
            print("Query did not complete successfully:", result_data)
            return
        
        total_results = result_data.get('totalArticles', 0)
        if total_results == 0:
            print("No results found.")
            return
        
        # Step 3: Paginate through the results and collect all articles.
        all_articles = fetch_all_articles(query_id, total_results, ATHENA_API_KEY)
        print("Total articles fetched:", len(all_articles))
    
    except requests.RequestException as e:
        print("An error occurred while communicating with the API:", e)

if __name__ == '__main__':
    main()
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
// Constants
const ATHENA_API_KEY = "YOUR_API_KEY";
const QUERY = "Tesla dealership protests";
const KEY_PHRASES = "('tesla takedown')";
const start_date = "2025-03-01T15:13:52.466Z";
const end_date = "2025-03-20T15:13:52.466Z";
const QUERY_URL = "https://app.runathena.com/api/v2/query-async";
const RESULTS_URL = "https://app.runathena.com/api/v2/get-results";
const HEADERS = { "Content-Type": "application/json" };
const ARTICLES_PER_PAGE = 25;
const POLL_INTERVAL = 1000; // milliseconds

// Helper sleep function
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function sendInitialQuery(query, apiKey) {
  const payload = {
    query: QUERY,
    key_phrases: KEY_PHRASES,
    api_key: apiKey,
    toggle_state: 'All Articles',
    start_date: start_date,
    end_date: end_date
  };

  const response = await fetch(QUERY_URL, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify(payload)
  });

  if (!response.ok) {
    throw new Error(`Network error: ${response.statusText}`);
  }

  const data = await response.json();
  return data.query_id;
}

async function pollForResults(queryId, apiKey, interval = POLL_INTERVAL) {
  const payload = {
    query_id: queryId,
    api_key: apiKey
  };

  let response = await fetch(RESULTS_URL, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify(payload)
  });
  if (!response.ok) {
    throw new Error(`Network error: ${response.statusText}`);
  }
  let data = await response.json();

  // Poll until the state is not 'PENDING'
  while (data.state === 'PENDING') {
    await sleep(interval);
    response = await fetch(RESULTS_URL, {
      method: "POST",
      headers: HEADERS,
      body: JSON.stringify(payload)
    });
    if (!response.ok) {
      throw new Error(`Network error: ${response.statusText}`);
    }
    data = await response.json();
  }

  return data;
}

async function fetchAllArticles(queryId, totalResults, apiKey) {
  let allArticles = [];
  let page = 1;

  // Continue fetching while there are articles remaining.
  while ((page - 1) * ARTICLES_PER_PAGE < totalResults) {
    const payload = {
      query_id: queryId,
      api_key: apiKey,
      toggle_state: 'All Articles',
      page: page
    };

    const response = await fetch(RESULTS_URL, {
      method: "POST",
      headers: HEADERS,
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      throw new Error(`Network error: ${response.statusText}`);
    }
    const data = await response.json();

    const articles = data.articles || [];
    allArticles.push(...articles);
    page++;
  }

  return allArticles;
}

async function main() {
  try {
    // Step 1: Send the initial query and retrieve the query_id.
    const queryId = await sendInitialQuery(QUERY, ATHENA_API_KEY);
    if (!queryId) {
      console.log("Failed to retrieve query ID.");
      return;
    }

    // Step 2: Poll the API until the query is processed.
    const resultData = await pollForResults(queryId, ATHENA_API_KEY);

    // Check if the query was successful before pagination.
    if (resultData.state !== 'SUCCESS') {
      console.log("Query did not complete successfully:", resultData);
      return;
    }

    const totalResults = resultData.totalArticles || 0;
    if (totalResults === 0) {
      console.log("No results found.");
      return;
    }

    // Step 3: Paginate through the results and collect all articles.
    const allArticles = await fetchAllArticles(queryId, totalResults, ATHENA_API_KEY);
    console.log("Total articles fetched:", allArticles.length);
  } catch (error) {
    console.error("An error occurred while communicating with the API:", error);
  }
}

main();

```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

// Constants
define('ATHENA_API_KEY', 'YOUR_API_KEY');
define('QUERY', 'Tesla dealership protests');
define('KEY_PHRASES', "('tesla takedown')");
define('START_DATE', '2025-03-01T15:13:52.466Z');
define('END_DATE', '2025-03-20T15:13:52.466Z');
define('QUERY_URL', 'https://app.runathena.com/api/v2/query-async');
define('RESULTS_URL', 'https://app.runathena.com/api/v2/get-results');
define('ARTICLES_PER_PAGE', 25);
define('POLL_INTERVAL', 1); // seconds

/**
 * Send the initial query and return the query_id.
 *
 * @param string $query
 * @param string $apiKey
 * @return string|null
 * @throws Exception if the request fails
 */
function sendInitialQuery($query, $apiKey) {
    $payload = [
        "query"         => $QUERY,
        "key_phrases"   => $KEY_PHRASES,
        "api_key"       => $apiKey,
        "toggle_state"  => "All Articles",
        "start_date"    => START_DATE,
        "end_date"      => END_DATE
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, QUERY_URL);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

    $response = curl_exec($ch);
    if (curl_errno($ch)) {
        throw new Exception('Request Error: ' . curl_error($ch));
    }
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode < 200 || $httpCode >= 300) {
        throw new Exception("HTTP error: $httpCode");
    }

    $data = json_decode($response, true);
    return isset($data['query_id']) ? $data['query_id'] : null;
}

/**
 * Polls the API until the query state is no longer 'PENDING'.
 *
 * @param string $queryId
 * @param string $apiKey
 * @param int $interval
 * @return array
 * @throws Exception if the request fails
 */
function pollForResults($queryId, $apiKey, $interval = POLL_INTERVAL) {
    $payload = [
        "query_id" => $queryId,
        "api_key"  => $apiKey
    ];

    do {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, RESULTS_URL);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new Exception('Request Error: ' . curl_error($ch));
        }
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode < 200 || $httpCode >= 300) {
            throw new Exception("HTTP error: $httpCode");
        }

        $data = json_decode($response, true);

        if (isset($data['state']) && $data['state'] === 'PENDING') {
            sleep($interval);
        } else {
            return $data;
        }
    } while (true);
}

/**
 * Fetches all articles by paginating through the results.
 *
 * @param string $queryId
 * @param int $totalResults
 * @param string $apiKey
 * @return array
 * @throws Exception if the request fails
 */
function fetchAllArticles($queryId, $totalResults, $apiKey) {
    $allArticles = [];
    $page = 1;

    while ((($page - 1) * ARTICLES_PER_PAGE) < $totalResults) {
        $payload = [
            "query_id"      => $queryId,
            "api_key"       => $apiKey,
            "toggle_state"  => "Encoded Articles",
            "page"          => $page
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, RESULTS_URL);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new Exception('Request Error: ' . curl_error($ch));
        }
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode < 200 || $httpCode >= 300) {
            throw new Exception("HTTP error: $httpCode");
        }

        $data = json_decode($response, true);
        $articles = isset($data['articles']) ? $data['articles'] : [];
        $allArticles = array_merge($allArticles, $articles);
        $page++;
    }
    return $allArticles;
}

function main() {
    try {
        // Step 1: Send the initial query and retrieve the query_id.
        $queryId = sendInitialQuery(QUERY, ATHENA_API_KEY);
        if (!$queryId) {
            echo "Failed to retrieve query ID.\n";
            return;
        }

        // Step 2: Poll the API until the query is processed.
        $resultData = pollForResults($queryId, ATHENA_API_KEY);

        // Check if the query was successful before pagination.
        if (!isset($resultData['state']) || $resultData['state'] !== 'SUCCESS') {
            echo "Query did not complete successfully:\n";
            print_r($resultData);
            return;
        }

        $totalResults = isset($resultData['totalArticles']) ? $resultData['totalArticles'] : 0;
        if ($totalResults == 0) {
            echo "No results found.\n";
            return;
        }

        // Step 3: Paginate through the results and collect all articles.
        $allArticles = fetchAllArticles($queryId, $totalResults, ATHENA_API_KEY);
        echo "Total articles fetched: " . count($allArticles) . "\n";

    } catch (Exception $e) {
        echo "An error occurred while communicating with the API: " . $e->getMessage() . "\n";
    }
}

main();

```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;

public class AthenaQuery {
    // Constants
    private static final String ATHENA_API_KEY = "YOUR_API_KEY";
    private static final String QUERY = "Tesla dealership protests";
    private static final String KEY_PHRASES = "('tesla takedown')"
    private static final String START_DATE = "2025-03-01T15:13:52.466Z";
    private static final String END_DATE = "2025-03-20T15:13:52.466Z";
    private static final String QUERY_URL = "https://app.runathena.com/api/v2/query-async";
    private static final String RESULTS_URL = "https://app.runathena.com/api/v2/get-results";
    private static final int ARTICLES_PER_PAGE = 25;
    private static final int POLL_INTERVAL = 1000; // milliseconds

    private static final HttpClient httpClient = HttpClient.newHttpClient();
    private static final ObjectMapper objectMapper = new ObjectMapper();

    // Sends the initial query and returns the query_id
    public static String sendInitialQuery(String query, String apiKey) throws Exception {
        String payload = String.format(
            "{\"query\":\"%s\", \"key_phrases\":\"%s\", \"api_key\":\"%s\", \"toggle_state\":\"All Articles\", \"start_date\":\"%s\", \"end_date\":\"%s\"}",
            QUERY, KEY_PHRASES,apiKey, START_DATE, END_DATE
        );

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(QUERY_URL))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        if(response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new RuntimeException("HTTP error: " + response.statusCode());
        }

        JsonNode jsonResponse = objectMapper.readTree(response.body());
        return jsonResponse.has("query_id") ? jsonResponse.get("query_id").asText() : null;
    }

    // Polls the API until the query state is no longer "PENDING"
    public static JsonNode pollForResults(String queryId, String apiKey) throws Exception {
        String payload = String.format("{\"query_id\":\"%s\", \"api_key\":\"%s\"}", queryId, apiKey);
        JsonNode data;
        do {
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(RESULTS_URL))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(payload))
                .build();

            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            if(response.statusCode() < 200 || response.statusCode() >= 300) {
                throw new RuntimeException("HTTP error: " + response.statusCode());
            }

            data = objectMapper.readTree(response.body());
            if (data.has("state") && data.get("state").asText().equals("PENDING")) {
                Thread.sleep(POLL_INTERVAL);
            } else {
                break;
            }
        } while (true);

        return data;
    }

    // Fetches and aggregates all articles by paginating through the results
    public static ArrayNode fetchAllArticles(String queryId, int totalResults, String apiKey) throws Exception {
        ArrayNode allArticles = objectMapper.createArrayNode();
        int page = 1;
        while ((page - 1) * ARTICLES_PER_PAGE < totalResults) {
            String payload = String.format(
                "{\"query_id\":\"%s\", \"api_key\":\"%s\", \"toggle_state\":\"All Articles\", \"page\":%d}",
                queryId, apiKey, page
            );

            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(RESULTS_URL))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(payload))
                .build();

            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            if(response.statusCode() < 200 || response.statusCode() >= 300) {
                throw new RuntimeException("HTTP error: " + response.statusCode());
            }

            JsonNode data = objectMapper.readTree(response.body());
            JsonNode articles = data.get("articles");
            if (articles != null && articles.isArray()) {
                allArticles.addAll((ArrayNode) articles);
            }
            page++;
        }
        return allArticles;
    }

    public static void main(String[] args) {
        try {
            // Step 1: Send the initial query and retrieve the query_id.
            String queryId = sendInitialQuery(QUERY, ATHENA_API_KEY);
            if (queryId == null || queryId.isEmpty()) {
                System.out.println("Failed to retrieve query ID.");
                return;
            }

            // Step 2: Poll the API until the query is processed.
            JsonNode resultData = pollForResults(queryId, ATHENA_API_KEY);
            if (!resultData.has("state") || !resultData.get("state").asText().equals("SUCCESS")) {
                System.out.println("Query did not complete successfully: " + resultData.toString());
                return;
            }

            int totalResults = resultData.has("totalArticles") ? resultData.get("totalArticles").asInt() : 0;
            if (totalResults == 0) {
                System.out.println("No results found.");
                return;
            }

            // Step 3: Paginate through the results and collect all articles.
            ArrayNode allArticles = fetchAllArticles(queryId, totalResults, ATHENA_API_KEY);
            System.out.println("Total articles fetched: " + allArticles.size());
        } catch (Exception e) {
            System.out.println("An error occurred while communicating with the API: " + e.getMessage());
        }
    }
}

```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'net/http'
require 'uri'
require 'json'

# Constants
ATHENA_API_KEY = "YOUR_API_KEY"
QUERY = "Tesla dealership protests"
KEY_PHRASES = "('tesla takedown')"
START_DATE = "2025-03-04T15:13:52.466Z"
END_DATE = "2025-03-05T15:13:52.466Z"
QUERY_URL = "https://app.runathena.com/api/v2/query-async"
RESULTS_URL = "https://app.runathena.com/api/v2/get-results"
ARTICLES_PER_PAGE = 25
POLL_INTERVAL = 1  # seconds

def send_initial_query(query, api_key)
  payload = {
    query: query,
    api_key: api_key,
    toggle_state: 'Encoded Articles',
    start_date: START_DATE,
    end_date: END_DATE
  }

  uri = URI.parse(QUERY_URL)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true if uri.scheme == 'https'
  request = Net::HTTP::Post.new(uri.request_uri, { 'Content-Type' => 'application/json' })
  request.body = payload.to_json

  response = http.request(request)
  if response.code.to_i < 200 || response.code.to_i >= 300
    raise "HTTP error: #{response.code}"
  end

  data = JSON.parse(response.body)
  data['query_id']
end

def poll_for_results(query_id, api_key)
  payload = { query_id: query_id, api_key: api_key }
  uri = URI.parse(RESULTS_URL)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true if uri.scheme == 'https'

  loop do
    request = Net::HTTP::Post.new(uri.request_uri, { 'Content-Type' => 'application/json' })
    request.body = payload.to_json

    response = http.request(request)
    if response.code.to_i < 200 || response.code.to_i >= 300
      raise "HTTP error: #{response.code}"
    end

    data = JSON.parse(response.body)
    if data['state'] == 'PENDING'
      sleep(POLL_INTERVAL)
    else
      return data
    end
  end
end

def fetch_all_articles(query_id, total_results, api_key)
  all_articles = []
  page = 1
  uri = URI.parse(RESULTS_URL)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true if uri.scheme == 'https'

  while (page - 1) * ARTICLES_PER_PAGE < total_results
    payload = {
      query_id: query_id,
      api_key: api_key,
      toggle_state: 'Encoded Articles',
      page: page
    }

    request = Net::HTTP::Post.new(uri.request_uri, { 'Content-Type' => 'application/json' })
    request.body = payload.to_json

    response = http.request(request)
    if response.code.to_i < 200 || response.code.to_i >= 300
      raise "HTTP error: #{response.code}"
    end

    data = JSON.parse(response.body)
    articles = data['articles'] || []
    all_articles.concat(articles)
    page += 1
  end

  all_articles
end

def main
  begin
    # Step 1: Send the initial query and retrieve the query_id.
    query_id = send_initial_query(QUERY, ATHENA_API_KEY)
    unless query_id
      puts "Failed to retrieve query ID."
      return
    end

    # Step 2: Poll the API until the query is processed.
    result_data = poll_for_results(query_id, ATHENA_API_KEY)
    unless result_data['state'] == 'SUCCESS'
      puts "Query did not complete successfully: #{result_data}"
      return
    end

    total_results = result_data['totalArticles'] || 0
    if total_results == 0
      puts "No results found."
      return
    end

    # Step 3: Paginate through the results and collect all articles.
    all_articles = fetch_all_articles(query_id, total_results, ATHENA_API_KEY)
    puts "Total articles fetched: #{all_articles.size}"
  rescue StandardError => e
    puts "An error occurred while communicating with the API: #{e.message}"
  end
end

main

```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://athena-10.gitbook.io/athena-api/getting-started/quickstart.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
