Chatbot queries by API

The AI Hub queries endpoint lets you integrate AI Hub chatbots into any workflow. You can send asynchronous queries, track their status, and retrieve responses.

This guide provides three script versions to help you get started.

  • Snippets - a sequence of code snippets to demonstrate API usage, omitting error handling for simplicity. Each snippet is presented and discussed in numbered steps below.

  • Complete script - all snippets are combined into a complete script for reference.

  • Enhanced script - an improved version of the complete script adds error handling and Python type annotations, resulting in near-production-quality code.

This guide uses Python, but you can access the queries endpoint with any language.

You can use the API to query any chatbot that you can interact with through the AI Hub user interface.

API chatbot queries are visible in your query history when accessing the chatbot from the AI Hub user interface.

Before you begin

Review the developer quickstart to ensure your account is set up correctly. Also review the authorization and context identification documentation, ensuring you have an API token and can define the IB-Context header.

1

Import modules

Start your Python script by importing required modules. The requests module makes it easier to send HTTP requests to the API.

1import json
2import time
3
4import requests # third-party module for making HTTP requests

If you don’t already have the requests module installed on your system, run pip install requests. There’s no harm in running this command even if the module is already installed.

2

Define URLs

You use the queries endpoint for all interactions with the chatbot, so you need to specify where to find that endpoint.

1# define URLs
2api_base_url = <API-BASE-URL>
3queries_endpoint_url = f'{api_base_url}/v2/queries'

Your API_BASE_URL depends on how you access AI Hub.

  • If you use SaaS AI Hub, set it to https://aihub.instabase.com/api.

  • If you run AI Hub on a custom domain, set it to that domain plus /api, such as https://www.acme-widgets.com/aihub/api.

3

Put authentication credentials in the request header

Every request to the AI Hub API must include authentication credential headers.

1# include authentication credentials as request headers
2api_token = <API-TOKEN>
3ib_context = <IB-CONTEXT>
4
5api_headers = {
6 'Authorization': f'Bearer {api_token}',
7 'IB-Context': ib_context
8}

To find values for API_TOKEN and IB_CONTEXT, see the Account setup section of the Developer quickstart.

4

Provide the chatbot ID

Identify the chatbot to query by providing its unique ID in a Python dictionary.

1# provide the chatbot ID
2source_app = {'type': 'CHATBOT',
3 'id': <CHATBOT-ID>}

For chatbot queries, the type key must have the string CHATBOT as its value.

The value for <CHATBOT-ID> is in the chatbot URL. The chatbot ID is the 36-character string at the end of the URL, excluding the slash at the beginning of the string.

To find the chatbot’s URL, navigate to Hub and run the chatbot you want to send queries to.

The chatbot ID embedded in the chatbot's URL

Chatbot ID embedded in the URL
A chatbot’s ID changes with every version update. For convenience, queries are automatically redirected to the latest version of the chatbot.
5

Create the request payload

Creating a chatbot query by API is an asynchronous operation.

  1. Send a request to the chatbot, with your query as its payload.

  2. Check whether the chatbot has finished processing the query by sending another request.

  3. If the chatbot is still processing, wait a few seconds and check its status again by sending another request.

  4. Repeat the previous step as many times as needed until the chatbot is done processing your query. That response that includes COMPLETE status also includes the chatbot’s reply to your query.

The next snippet sends the request described in step one above. The API responds with a query ID that you must include in your processing status requests.

The initial request includes the query and key data about the chatbot. Store that information in a Python dictionary so you can include it in the request.

1# create payload for POST to 'queries' endpoint
2data = {'query': <QUERY>,
3 'model_name': <MODEL-NAME>,
4 'include_source_info': <INCLUDE-SOURCE-INFO>,
5 'source_app': source_app}

User-specified values

PlaceholderTypeRequiredDescription
querystrYesThe question to submit to the chatbot. This can be any query you would submit through the graphical user interface, excluding requests for graphs.
model_namestrNoModel for the chatbot to use when answering your query.
- multistep-lite for basic queries
- multistep for complicated queries
Defaults to multistep.
The multistep model takes more processing time and uses more consumption units, but can give better answers to difficult questions.
include_source_infoboolNoWhether the reply includes information about which documents it was compiled from. Defaults to False.
When model_name is set to multistep-lite, source info includes document names only. When model_name is set to multistep, source info includes document names and page numbers.
source_appdict[str, str]YesSee the Provide the chatbot ID section above.
6

Submit the query and capture the query_id

You’re ready to submit your query to a chatbot as an HTTP request. The HTTP response to this request contains a query ID that you must include in future requests to check the chatbot’s processing status.

The word response can be confusing in this context. This documentation uses response to mean the HTTP response to an HTTP request, and reply to mean the chatbot’s answer to your query.
1# send the query to the chatbot and capture the 'query_id'
2response = requests.post(url=queries_endpoint_url,
3 headers=api_headers,
4 data=json.dumps(data))
5resp_data = response.json()
6query_id = resp_data.get('query_id')

User-defined values

ParameterTypeRequiredDescription
headersdictYesSee Put authentication credentials in the request header section above

The response is a JSON object containing the query ID, for example:

1{ "query_id": "f2ef9702-b018-4a45-9b2e-d1fb575b42ed" }
7

Get the query processing status and query reply

After sending your query, you must send a separate request to retrieve the chatbot’s reply. Keep calling the get chatbot query status endpoint until the status returned in the response changes from RUNNING to COMPLETE. When that happens, the chatbot’s reply to your query is included in the most recent response.

1# get the status of the query
2while True:
3 response = requests.get(url=f'{queries_endpoint_url}/{query_id}',
4 headers=api_headers)
5 response_json = response.json()
6 status = response_json.get('status')
7 if status == 'COMPLETE':
8 break
9 time.sleep(5)

User-defined parameters

ParameterTypeRequiredDescription
query_idstrYesThe ID of the query. This value is retrieved from the response to the request made in the Submit the query and capture the query_id section above.

The response body is a JSON object containing the query ID and status. If the query status is COMPLETE, the query response is also returned. If the query status is FAILED, an error message is returned instead.

Sample JSON object in response

1{
2 "query_id": "f2ef9702-b018-4a45-9b2e-d1fb575b42ed",
3 "status": "COMPLETE",
4 "results": [
5 {
6 "response":" "<REPLY>",
7 "source_documents": [
8 {
9 "name": "file1.pdf",
10 "pages": [
11 {
12 "bboxes": [],
13 "page_number": 9.0
14 }
15 ]
16 },
17 {
18 "name": "file2.pdf",
19 "pages": [
20 {
21 "bboxes": [],
22 "page_number": 2.0
23 }
24 ]
25 }
26 ]
27 }
28 ]
29}

JSON schema

Because the snippet above converts the JSON object into a Python dictionary, the types listed in this schema are Python types, not JSON types.

KeyPython typeDescription
query_idstrID of the query.
statusstrStatus of the query.
Possible values are RUNNING, COMPLETE, or FAILED.
resultslist[dict]Container for query replies and data sources. Returned if the status field is COMPLETE.
results.responsestrChatbot’s reply to your query.
results.source_documentslist[dict]Container for information about the documents the chatbot used when composing its reply to your query.
Returned if include_source_info was set to true in the data dictionary submitted as the payload of the original query request.
results.source_documents[i].namestrThe name of a source document that contributed to the chatbot’s reply.
results.source_documents[i].pageslist[dict]A list of objects with information about the specific pages referenced in the source document.
Page-level source information is supported only when using the multistep model.
results.source_documents[i].pages[j].page_numberfloatThe page from the source document that contributed to the query’s reply.
results.source_documents[i].pages[j].bboxeslistBounding boxes from the source document, for the text that contributed to the query’s reply. Not always applicable.
errorobjectPresent only if the status is FAILED. Contains a message with information about the source of the execution error.
8

Parse the Python dictionary that contains the JSON object to extract and print the reply to your query and any data sources the chatbot used.

1# print the query's reply and its sources
2results = response_json['results']
3for result in results:
4 reply = result['response']
5 print(f"REPLY\n{reply}\n")
6
7 sources = result['source_documents']
8 print("SOURCE DOCUMENTS")
9 for source in sources:
10 print(f"{source['name']}")
11 pages = source['pages']
12 for page in pages:
13 page = int(page['page_number']) # convert from float
14 print(f" page {page}")

Complete script

This example Python script shows a complete, end-to-end workflow based on calls to the queries endpoint. The code is deliberately minimal, to make it easier to learn how to use the API.

The script performs the following tasks:

  1. Call the send chatbot query endpoint, sending an asynchronous request to query a specified chatbot. A query ID is returned.

  2. Poll the get chatbot query status endpoint repeatedly, until the chatbot’s processing status changes from RUNNING to COMPLETE.

  3. Extract and print the reply and sources from the last response.

1import json
2import time
3
4import requests # third-party module for making HTTP requests
5
6# define URLs
7api_base_url = <API-BASE-URL>
8queries_endpoint_url = f'{api_base_url}/v2/queries'
9
10# include authentication credentials as request headers
11api_token = <API-TOKEN>
12ib_context = <IB-CONTEXT>
13
14api_headers = {
15 'Authorization': f'Bearer {api_token}',
16 'IB-Context': ib_context
17}
18
19# provide the chatbot ID
20source_app = {'type': 'CHATBOT',
21 'id': <CHATBOT-ID>}
22
23# create payload for POST to 'queries' endpoint
24data = {'query': <QUERY>,
25 'model_name': <MODEL-NAME>,
26 'include_source_info': <INCLUDE-SOURCE-INFO>,
27 'source_app': source_app}
28
29# send the query to the chatbot and capture the 'query_id'
30response = requests.post(url=queries_endpoint_url,
31 headers=api_headers,
32 data=json.dumps(data))
33resp_data = response.json()
34query_id = resp_data.get('query_id')
35
36# get the status of the query
37while True:
38 response = requests.get(url=f'{queries_endpoint_url}/{query_id}',
39 headers=api_headers)
40 response_json = response.json()
41 status = response_json.get('status')
42 if status == 'COMPLETE':
43 break
44 time.sleep(5)
45
46# print the query's reply and its sources
47results = response_json['results']
48for result in results:
49 reply = result['response']
50 print(f"REPLY\n{reply}\n")
51
52 sources = result['source_documents']
53 print("SOURCE DOCUMENTS")
54 for source in sources:
55 print(f"{source['name']}")
56 pages = source['pages']
57 for page in pages:
58 page = int(page['page_number']) # convert from float
59 print(f" page {page}")

User-defined values

PlaceholderTypeRequiredDescription
api_tokenstrYesYour API token. Used when initializing the API client.
api_rootstrNoYour AI Hub root URL. Used when initializing the API client.
Community accounts: omit setting api_root.
Organization accounts:
- If your organization has a custom AI Hub domain, use your organization’s root API URL, such as https://my-org.instabase.com/api.
- If your organization doesn’t have a custom AI Hub domain, omit setting api_root.
ib_contextstrNo, but recommendedUsed when initializing the API client.
If ib_context isn’t explicitly set, requests use consumption units from your community account.
querystrYesThe question to submit to the chatbot. This can be any query you would submit through the graphical user interface, excluding requests for graphs.
model_namestrNoModel for the chatbot to use when answering your query.
- multistep-lite for basic queries
- multistep for complicated queries
Defaults to multistep.
The multistep model takes more processing time and uses more consumption units, but can give better answers to difficult questions.
include_source_infoboolNoWhether the reply includes information about which documents it was compiled from. Defaults to False.
When model_name is set to multistep-lite, source info includes document names only. When model_name is set to multistep, source info includes document names and page numbers.
source_appdict[str, str]YesSee the Provide the chatbot ID section above.

Enhanced script

This script is functionally identical to the complete script above but adds error handling and Python type annotation, bringing it closer to production-grade code.

The SDK supports Python 3.7+, but this script uses improved type annotations introduced in Python 3.9. To run this code on Python 3.7 or 3.8, remove or modify the type annotations.

The error handling in this script shows what can go wrong, but the recovery code is unrealistically basic. Replace sys.exit() calls with error handling that’s appropriate for your workflow.

1import json
2import sys
3import time
4from typing import Any, Final, Literal, Optional
5
6import requests # third-party module for making HTTP requests
7from requests import Response
8
9# define URLs
10API_BASE_URL: Final[str] = <API-BASE-URL>
11QUERIES_ENDPOINT_URL: Final[str] = f'{API_BASE_URL}/v2/queries'
12
13# how long to wait for the Chatbot to process the query
14# and generate a reply
15PROCESSING_TIMEOUT_SECS: Final[int] = <PROCESSING-TIMEOUT-SECS>
16
17# how long to wait for an HTTP response to each HTTP request
18RESPONSE_TIMEOUT_SECS: Final[int] = <RESPONSE-TIMEOUT-SECS>
19
20# how long to wait between query processing status checks
21STATUS_POLLING_INTERVAL_SECS: Final[int] = <STATUS-POLLING-INTERVAL-SECS>
22
23# put authentication credentials in a header for API calls
24API_TOKEN: Final[str] = <API-TOKEN>
25IB_CONTEXT: Final[str] = <IB-CONTEXT>
26API_HEADERS: Final[dict[str, str]] = {
27 'Authorization': f'Bearer {API_TOKEN}',
28 'IB-Context': IB_CONTEXT
29}
30
31# provide the chatbot ID
32SOURCE_APP: Final[dict[str, str]] = {'type': 'CHATBOT',
33 'id': <CHATBOT-ID>}
34
35# create payload for POST to 'queries' endpoint
36data: dict[str, Any] = {'query': <QUERY>,
37'model_name': <MODEL-NAME>,
38'include_source_info': <INCLUDE-SOURCE-INFO>,
39'source_app': SOURCE_APP}
40
41# send the query to the chatbot and capture the 'query_id'
42try:
43 response: Response = requests.post(url=QUERIES_ENDPOINT_URL,
44 headers=API_HEADERS,
45 data=json.dumps(data),
46 timeout=RESPONSE_TIMEOUT_SECS)
47except requests.exceptions.ConnectionError as conn_err:
48 sys.exit(f"Error: Couldn't submit request to API: {conn_err}")
49except requests.exceptions.Timeout as timeout_err:
50 sys.exit(f"Error: Request to API timed out: {timeout_err}")
51
52if not response.ok:
53 sys.exit("Error: Bad response from API. "
54 f"Response status code: {response.status_code}. "
55 f"Response content: {response.text}")
56
57response_json: dict[str, Any] = response.json()
58query_id: Optional[str] = response_json.get('query_id')
59
60if not query_id:
61 sys.exit("Error: Chatbot didn't return a query_id")
62
63# get the query status and check for chatbot processing timeout
64elapsed_time_secs: int = 0
65while elapsed_time_secs < PROCESSING_TIMEOUT_SECS:
66 try:
67 response = requests.get(url=f'{QUERIES_ENDPOINT_URL}/{query_id}',
68 headers=API_HEADERS)
69 except requests.exceptions.ConnectionError as conn_err:
70 sys.exit(f"Error: Couldn't submit request to API: {conn_err}")
71 except requests.exceptions.Timeout as timeout_err:
72 sys.exit(f"Error: Request to API timed out: {timeout_err}")
73 except Exception as err:
74 sys.exit(f"Error: Request to API failed: {err}")
75
76 # check if we got back an expected status code
77 if response.ok:
78 response_json = response.json()
79 else:
80 sys.exit("Error: Bad response from API. "
81 f"Response status code: {response.status_code}. "
82 f"Response content: {response.text}")
83
84 # stop polling once the processing status is 'COMPLETE'
85 status: Optional[Literal['COMPLETE', 'FAILED', 'RUNNING']] = response_json.get('status')
86 if status == 'COMPLETE':
87 break
88 time.sleep(STATUS_POLLING_INTERVAL_SECS)
89 elapsed_time_secs += STATUS_POLLING_INTERVAL_SECS
90
91# handle processing timeout
92if elapsed_time_secs >= PROCESSING_TIMEOUT_SECS:
93 sys.exit(f"Error: Chatbot didn't finish processing the query "
94 f"within {PROCESSING_TIMEOUT_SECS} seconds")
95
96# Print the reply and its sources. Assume the API returns a well-
97# formed reply, so no need for error handling when parsing the reply
98results: list[dict] = response_json['results']
99result: dict[str, Any]
100for result in results:
101 reply: str = result['response']
102 print(f"REPLY\n{reply}\n")
103 sources: list[dict[str, Any]] = result['source_documents']
104 if sources:
105 print("SOURCE DOCUMENTS")
106 source: dict[str, Any]
107 for source in sources:
108 print(f"{source['name']}")
109 pages: list[dict] = source['pages']
110 page: dict[str, Any]
111 for page in pages:
112 page_num: int = int(page['page_number'])
113 print(f" page {page_num}")

User-defined values

Most of the variables or parameters in this script are described in the User-defined values section above, but these three constants are only in the enhanced script.

ConstantTypeRequiredDescription
PROCESSING-TIMEOUT-SECSintYesSeconds to wait for the chatbot to compose a reply to your query. Recommended value: 45
RESPONSE-TIMEOUT-SECSintYesSeconds to wait for an HTTP response to each HTTP request. Recommended value: 3
STATUS-POLLING-INTERVAL-SECSintYesSeconds to wait between checking the processing status of a query. Recommended value: 5
Was this page helpful?