Quickstart#
This tutorial will help you get started with the Tonita API through our Python library. We will walk through an example of how listings and corpora can be used to prepare and organize data. We will then go over some of the ways in which search can be performed, as well as how search results can be evaluated.
Before proceeding with the tutorial, make sure that you have an API key (see Authentication) and that the Tonita Python library is installed (see Installation). It would also be helpful to review Key concepts.
Note
In addition to the Tonita Python library, we also provide HTTP endpoints. While this tutorial will focus primarily on our Python library, we also offer examples that make requests to these HTTP endpoints directly using curl. See Making HTTP requests for more information.
Setting a default API key#
We first import the client library and set a default API key for API calls.
import tonita
tonita.api_key = "my_api_key"
This signals that subsequent calls that do not provide an API key will default to this value instead. See API keys and corpus IDs for other ways the API key can be set in the Python library.
Creating a corpus#
We must first create a corpus to populate with listings. To add a corpus, simply make the following call:
tonita.corpora.add(corpus_id="nyc_housing")
# Example return value:
# AddCorpusResponse(corpus_id='nyc_housing')
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-d '{"corpus_id": "nyc_housing"}' \
https://api.tonita.co/corpora/add
# Example response value:
# {"corpus_id": "nyc_housing"}
AddCorpusResponse
is a Python dataclass that contains the response data for your request to add a corpus. Each call to a Tonita API endpoint using the Python library will return a dataclass containing the response data. See Request and response types types for more information.
We can then confirm its existence by listing all corpora associated with this API key:
tonita.corpora.list()
# Example return value:
# ListCorporaResponse(results={"nyc_housing": <State.ACTIVE: 'ACTIVE'>})
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
https://api.tonita.co/corpora/list
# Example response value:
# {"results": {"nyc_housing": "ACTIVE"}}
Or check that, in particular, the corpus we just created exists:
tonita.corpora.get(corpus_id="nyc_housing")
# Example return value:
# GetCorpusResponse(
# corpus_id='nyc_housing',
# exists=True,
# state=<State.ACTIVE: 'ACTIVE'>,
# seconds_to_expiration=None
# )
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-d '{"corpus_id": "nyc_housing"}' \
https://api.tonita.co/corpora/get
# Example response value:
# {
# "corpus_id": "nyc_housing",
# "exists": true,
# "state": "ACTIVE",
# "seconds_to_expiration": null
# }
To create another corpus, simply make another call to tonita.corpora.add
. Note that corpus IDs must be unique for a given API key.
tonita.corpora.add(corpus_id="movies")
# Example return value:
# AddCorpusResponse(corpus_id='movies')
tonita.corpora.list()
# Example return value:
# ListCorporaResponse(
# results={
# "movies": <State.ACTIVE: 'ACTIVE'>,
# "nyc_housing": <State.ACTIVE: 'ACTIVE'>
# }
# )
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-d '{"corpus_id": "movies"}' \
https://api.tonita.co/corpora/add
# Example response value:
# {"corpus_id": "movies"}
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
https://api.tonita.co/corpora/list
# Example response value:
# {
# "results": {
# "movies": "ACTIVE",
# "nyc_housing": "ACTIVE"
# }
# }
Adding listings#
We can now add listings to our corpora.
As with the API key, we can set a default corpus ID for subsequent calls using the Python library.
tonita.corpus_id = "nyc_housing"
tonita.listings.add(
data={
"h388387ds": {
"data": {
"bedrooms": 5,
"bathrooms": 4,
"in_unit_laundry": True,
"balcony": True,
"neighborhood": "Upper East Side",
"price": 7000,
"description": [
"Peaceful, tree-lined street.",
"Lush green spaces everywhere in the neighborhood."
]
},
"categories": ["townhouse"]
}
}
)
# Expected return value:
# AddListingsResponse(
# results={
# "h388387ds": AddSingleListingResult(success=True, error_message="")
# }
# )
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-H "tonita-corpus-id: nyc_housing" \
-d '{"h388387ds":{"data":{"bedrooms":5,"bathrooms":4,"in_unit_laundry":true,"balcony":true,"neighborhood":"Upper East Side","price":7000,"description":["Peaceful, tree-lined street.","Lush green spaces everywhere in the neighborhood."]},"categories":["townhouse"]}}' \
https://api.tonita.co/listings/add
# Example response value:
# {"results": {"h388387ds": {"success": true, "error_message": ""}}}
Let’s break this request down. Note that the argument passed to tonita.listings.add()
is a dictionary. The keys in this dictionary are the IDs of listings. (Listing IDs must be unique within a corpus.) The sole listing we’re adding to our corpus in the above example has ID "h388387ds"
.
Each listing ID in turn maps to another dictionary with two keys: "data"
and "categories"
. In the above example, the sole listing "h388387ds"
maps to the following dictionary:
{
"data": {
"bedrooms": 5,
"bathrooms": 4,
"in_unit_laundry": True,
"balcony": True,
"neighborhood": "Upper East Side",
"price": 7000,
"description": [
"Peaceful, tree-lined street.",
"Lush green spaces everywhere in the neighborhood."
]
}
"categories": ["townhouse"]
}
This dictionary has the following keys:
The value for the
"data"
key (required) will be a dictionary that contains the data for the listing. Here, we have the size of the home, its neighborhood, its price, amenity information, and some strings desribing the listing. You may provided whatever keys and data you like. Note that the more informative the data that you provide, the better the search experience will be.The value for the
"categories"
key (optional) will be a list of one or more category strings that can be attached to each listing, and can be used to restrict searches within a corpus by specifying the list of categories allowed during search. What constitutes a category is entirely up to you; a product search engine may use categories to represent different types of products (e.g., household appliances, furniture, clothing, etc.), while a vacation rental company may use categories to represent different cities, or even neighborhoods within a city.
Now that we’ve added a listing to the corpus, we can confirm that this listing exists by viewing all of the listings in this corpus…
tonita.listings.list()
# Example return value:
# ListListingsResponse(
# results={"h388387ds": <State.ACTIVE: 'ACTIVE'>}, next_listing_id=None
# )
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-H "tonita-corpus-id: nyc_housing" \
https://api.tonita.co/listings/list
# Example response value:
# {"results": {"h388387ds": "ACTIVE"}, "next_listing_id": null}
…and view its data.
tonita.listings.get(listing_ids=["h388387ds"])
# Example return value:
# GetListingsResponse(
# results={
# "h388387ds": GetSingleListingResult(
# success=True,
# data={
# "data": {
# "bedrooms": 5,
# "bathrooms": 4,
# "in_unit_laundry": True,
# "balcony": True,
# "neighborhood": "Upper East Side",
# "price": 7000,
# "description": [
# "Peaceful, tree-lined street.",
# "Lush green spaces everywhere in the neighborhood."
# ]
# },
# "categories": ["townhouse"]
# },
# state=<State.ACTIVE: 'ACTIVE'>,
# seconds_to_expiration=None,
# error_message=""
# )
# }
# )
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-H "tonita-corpus-id: nyc_housing" \
-d '{"listing_id": "h388387ds"}' \
https://api.tonita.co/listings/get
# Example response value:
# {
# "results": {
# "h388387ds": {
# "success": true,
# "data": {
# "data": {
# "bedrooms": 5,
# "bathrooms": 4,
# "in_unit_laundry": true,
# "balcony": true,
# "neighborhood": "Upper East Side",
# "price": 7000,
# "description": [
# "Peaceful, tree-lined street.",
# "Lush green spaces everywhere in the neighborhood."
# ]
# },
# "categories": [
# "townhouse"
# ],
# },
# "state": "ACTIVE",
# "seconds_to_expiration": null,
# "error_message": ""
# }
# }
# }
Multiple listings can be added at the same time:
tonita.listings.add(
data={
"h197233sm": {
"data": {
"bedrooms": 1,
"bathrooms": 1,
"in_unit_laundry": False,
"neighborhood": "Hell's Kitchen",
"price": 400,
"description": [
"Oversized one-bedroom.",
"Plenty of train lines in the area.",
"Bustling nightlife scene."
]
},
"categories": ["apartment", "co-op"]
},
"h298921md": {
"data": {
"bedrooms": 1,
"bathrooms": 1,
"in_unit_laundry": True,
"neighborhood": "Williamsburg",
"price": 800,
"description": [
"South and east exposures.",
"Great restaurants nearby.",
"Steps from McCarren Park."
]
},
"categories": ["apartment", "condo"]
}
}
)
# Example return value:
# AddListingsResponse(
# results={
# "h197233sm": AddSingleListingResult(success=True, error_message=""),
# "h298921md": AddSingleListingResult(success=True, error_message="")
# }
# )
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-H "tonita-corpus-id: nyc_housing" \
-d '{"h197233sm":{"data":{"bedrooms":0,"bathrooms":1,"in_unit_laundry":false,"neighborhood":"Hell's Kitchen","price":400,"description":["Very big studio apartment in the back of building.","Plenty of train lines in the area.","Bustling nightlife scene."]},"categories":["apartment","co-op"]},"h298921md":{"data":{"bedrooms":2,"bathrooms":2,"in_unit_laundry":true,"neighborhood":"Williamsburg","price":800,"description":["South and east exposures.","Great restaurants nearby.","Steps from McCarren Park."]},"categories":["apartment","condo"]}}' \
https://api.tonita.co/listings/add
# Example response value:
# {
# "results": {
# "h197233sm": {
# "success": true,
# "error_message": ""
# },
# "h298921md": {
# "success": true,
# "error_message": ""
# }
# }
# }
Here, we’re adding two listings: "h197233sm"
and "h298921md"
.
Tip
Listings can also be added by reading listings data from a file. See Managing listings for more details.
Let’s confirm that our "nyc_housing"
corpus now has three listings:
tonita.listings.list()
# Example return value:
# ListListingsResponse(
# results={
# "h197233sm": <State.ACTIVE: 'ACTIVE'>,
# "h298921md": <State.ACTIVE: 'ACTIVE'>,
# "h388387ds": <State.ACTIVE: 'ACTIVE'>
# },
# next_listing_id=None
# )
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-H "tonita-corpus-id: nyc_housing" \
https://api.tonita.co/listings/list
# Example response value:
# {
# "results": {
# "h197233sm": "ACTIVE",
# "h298921md": "ACTIVE",
# "h388387ds": "ACTIVE"
# }
# "next_listing_id": null
# }
Listings can, of course, be deleted and updated. See Managing listings for more details.
Performing search#
Tonita will work with you to learn how to best process the data that you’ve provided, and build a search engine for you. This will take some time; we will notify you when a search engine is ready. (If you wish to obtain the models and integrate with your existing search engine, reach out to us at hello@tonita.co, and we’d be happy to work with you.)
Once we’ve provided you with a search engine, you can perform search.
For example, suppose we want to perform search over the listings in our "nyc_housing"
corpus. We’d simply call tonita.search()
:
tonita.search(
query='sunny 1 bedroom on a quiet street near parks',
max_results=2,
categories=["co-op", "condo"]
)
curl -X POST -H "content-type: application/json" \
-H "x-api-key: my_api_key" \
-H "tonita-corpus-id: nyc_housing" \
-d '{"query": "sunny 1 bedroom on a quiet street near parks", "max_results": 2, "categories": ["co-op", "condo"]}' \
https://api.tonita.co/search
In this example, we simply want the ten listings most relevant to our given query string, but the Tonita search API provides a host of ways in which search can be performed: you can also search for similar listings by providing a listing ID, and there are options for restricting search by category and facet values. See our guide on performing search for details.
The response will be returned in a SearchResponse
dataclass. Let’s look at one sample response.
SearchResponse(
items=[
SearchResponseItem(
listing_id="h298921md",
score=1.172,
categories=["apartment", "condo"],
snippets=[
Snippet(
display_string="South and east exposures."
),
Snippet(
display_string="Steps from McCarren Park."
)
],
),
SearchResponseItem(
listing_id="h197233sm",
score=0.239,
categories=["apartment", "co-op"],
snippets=[
Snippet(
display_string="Oversized one-bedroom."
)
]
)
]
)
{
"items": [
{
"listing_id": "h298921md",
"score": 1.172,
"categories": [
"apartment",
"condo"
],
"snippets": [
{
"display_string": "South and east exposures."
},
{
"display_string": "Steps from McCarren Park."
}
]
},
{
"listing_id": "h197233sm",
"score": 0.239,
"categories": [
"apartment",
"co-op"
],
"snippets": [
{
"display_string": "Oversized one-bedroom."
}
]
}
]
}
Here, two relevant listings were returned. Note that the response sorts listing results in descending order of relevance score, and returns each listing ID with its score, the matching categories, and some strings (“snippets”) that explain why that result was returned. For more details about this response object, see our guide on performing search.
And voilà! We’ve completed our first search.