Search
curl --request POST \
--url https://index.trygravity.ai/search \
--header 'Content-Type: application/json' \
--data '
{
"query": "<string>",
"search_id": "<string>",
"context": {},
"platform_api_key": "<string>",
"external_session_id": "<string>",
"external_user_id_hash": "<string>",
"external_user_id": "<string>",
"hashed_email": "<string>",
"metadata": {},
"monetization": {}
}
'import requests
url = "https://index.trygravity.ai/search"
payload = {
"query": "<string>",
"search_id": "<string>",
"context": {},
"platform_api_key": "<string>",
"external_session_id": "<string>",
"external_user_id_hash": "<string>",
"external_user_id": "<string>",
"hashed_email": "<string>",
"metadata": {},
"monetization": {}
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: '<string>',
search_id: '<string>',
context: {},
platform_api_key: '<string>',
external_session_id: '<string>',
external_user_id_hash: '<string>',
external_user_id: '<string>',
hashed_email: '<string>',
metadata: {},
monetization: {}
})
};
fetch('https://index.trygravity.ai/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://index.trygravity.ai/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => '<string>',
'search_id' => '<string>',
'context' => [
],
'platform_api_key' => '<string>',
'external_session_id' => '<string>',
'external_user_id_hash' => '<string>',
'external_user_id' => '<string>',
'hashed_email' => '<string>',
'metadata' => [
],
'monetization' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://index.trygravity.ai/search"
payload := strings.NewReader("{\n \"query\": \"<string>\",\n \"search_id\": \"<string>\",\n \"context\": {},\n \"platform_api_key\": \"<string>\",\n \"external_session_id\": \"<string>\",\n \"external_user_id_hash\": \"<string>\",\n \"external_user_id\": \"<string>\",\n \"hashed_email\": \"<string>\",\n \"metadata\": {},\n \"monetization\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://index.trygravity.ai/search")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"<string>\",\n \"search_id\": \"<string>\",\n \"context\": {},\n \"platform_api_key\": \"<string>\",\n \"external_session_id\": \"<string>\",\n \"external_user_id_hash\": \"<string>\",\n \"external_user_id\": \"<string>\",\n \"hashed_email\": \"<string>\",\n \"metadata\": {},\n \"monetization\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://index.trygravity.ai/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"<string>\",\n \"search_id\": \"<string>\",\n \"context\": {},\n \"platform_api_key\": \"<string>\",\n \"external_session_id\": \"<string>\",\n \"external_user_id_hash\": \"<string>\",\n \"external_user_id\": \"<string>\",\n \"hashed_email\": \"<string>\",\n \"metadata\": {},\n \"monetization\": {}\n}"
response = http.request(request)
puts response.read_body{
"search_id": "<string>",
"recommendation": {
"name": "<string>",
"slug": "<string>",
"category": "<string>",
"description": "<string>",
"website_url": "<string>",
"docs_url": "<string>"
},
"reasoning": "<string>",
"install": {
"summary": "<string>",
"steps": [
{}
],
"env_vars": [
{}
],
"notes": "<string>"
},
"credential_request": {
"setup_url": "<string>",
"required_env_vars": [
{}
],
"user_message": "<string>",
"agent_instruction": "<string>",
"grclid": "<string>"
},
"click_url": "<string>",
"grclid": "<string>"
}Interfaces
Search
Find the best tool for a development task
POST
/
search
Search
curl --request POST \
--url https://index.trygravity.ai/search \
--header 'Content-Type: application/json' \
--data '
{
"query": "<string>",
"search_id": "<string>",
"context": {},
"platform_api_key": "<string>",
"external_session_id": "<string>",
"external_user_id_hash": "<string>",
"external_user_id": "<string>",
"hashed_email": "<string>",
"metadata": {},
"monetization": {}
}
'import requests
url = "https://index.trygravity.ai/search"
payload = {
"query": "<string>",
"search_id": "<string>",
"context": {},
"platform_api_key": "<string>",
"external_session_id": "<string>",
"external_user_id_hash": "<string>",
"external_user_id": "<string>",
"hashed_email": "<string>",
"metadata": {},
"monetization": {}
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: '<string>',
search_id: '<string>',
context: {},
platform_api_key: '<string>',
external_session_id: '<string>',
external_user_id_hash: '<string>',
external_user_id: '<string>',
hashed_email: '<string>',
metadata: {},
monetization: {}
})
};
fetch('https://index.trygravity.ai/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://index.trygravity.ai/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => '<string>',
'search_id' => '<string>',
'context' => [
],
'platform_api_key' => '<string>',
'external_session_id' => '<string>',
'external_user_id_hash' => '<string>',
'external_user_id' => '<string>',
'hashed_email' => '<string>',
'metadata' => [
],
'monetization' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://index.trygravity.ai/search"
payload := strings.NewReader("{\n \"query\": \"<string>\",\n \"search_id\": \"<string>\",\n \"context\": {},\n \"platform_api_key\": \"<string>\",\n \"external_session_id\": \"<string>\",\n \"external_user_id_hash\": \"<string>\",\n \"external_user_id\": \"<string>\",\n \"hashed_email\": \"<string>\",\n \"metadata\": {},\n \"monetization\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://index.trygravity.ai/search")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"<string>\",\n \"search_id\": \"<string>\",\n \"context\": {},\n \"platform_api_key\": \"<string>\",\n \"external_session_id\": \"<string>\",\n \"external_user_id_hash\": \"<string>\",\n \"external_user_id\": \"<string>\",\n \"hashed_email\": \"<string>\",\n \"metadata\": {},\n \"monetization\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://index.trygravity.ai/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"<string>\",\n \"search_id\": \"<string>\",\n \"context\": {},\n \"platform_api_key\": \"<string>\",\n \"external_session_id\": \"<string>\",\n \"external_user_id_hash\": \"<string>\",\n \"external_user_id\": \"<string>\",\n \"hashed_email\": \"<string>\",\n \"metadata\": {},\n \"monetization\": {}\n}"
response = http.request(request)
puts response.read_body{
"search_id": "<string>",
"recommendation": {
"name": "<string>",
"slug": "<string>",
"category": "<string>",
"description": "<string>",
"website_url": "<string>",
"docs_url": "<string>"
},
"reasoning": "<string>",
"install": {
"summary": "<string>",
"steps": [
{}
],
"env_vars": [
{}
],
"notes": "<string>"
},
"credential_request": {
"setup_url": "<string>",
"required_env_vars": [
{}
],
"user_message": "<string>",
"agent_instruction": "<string>",
"grclid": "<string>"
},
"click_url": "<string>",
"grclid": "<string>"
}The core endpoint. An AI reasoning agent analyzes your query against the catalog and returns the best match with an explanation and install instructions.
Every matched response is complete and actionable — install steps, env vars, and a tracked click link are included when the recommended service has a destination URL.
The Index remembers the previous conversation and answers in context.
Authentication
Send your publisher key as theX-API-Key header or as the platform_api_key body field. One of the
two is required.
string
Publisher API key. Alternative to the
platform_api_key body field.Request
string
required
What you need. Natural language — e.g. “serverless database with free tier” or “auth for Next.js with SSO”
string
Pass a
search_id from a previous response to ask a follow-up question. The Index maintains conversation context.object
Additional project context. Framework, language, constraints, etc.
string
Publisher API key for attribution. Alternative to the
X-API-Key header — send one or the other.string
Your stable chat/session ID. Stored for attribution; not used for recommendation reasoning.
string
Hash of your user ID. Prefer this over sending a raw user identifier.
string
Optional raw user ID. Gravity stores only a SHA-256 hash.
string
SHA-256 of the user’s normalized email (
email.strip().lower()), when available. This is the canonical field and matches the ad request convention. The legacy email_hash field is still accepted as an alias.object
Non-sensitive publisher metadata such as surface, workspace, plan, or placement. Stored with the search for attribution/debugging; not used in the LLM prompt.
object
Optional private publisher ranking controls. Requires
platform_api_key. Supported mode: boost_cpa — relevance-first ranking that gives a small boost to services with an active CPA campaign. Omit (or use off) for pure relevance. Economics are not returned in the response.Response
string
Unique ID for this search. Pass it back for follow-ups.
object
string
Why this tool is the best fit for your specific query. Natural language explanation.
object
object
Explicit handoff instructions for credentials the human user must fetch.
string
Tracked short link the user visits to get their API credentials.
string
Gravity Index click ID minted for the tracked click link.
Examples
Basic search
curl -X POST https://index.trygravity.ai/search \
-H "Content-Type: application/json" \
-H "X-API-Key: $GRAVITY_PUBLISHER_KEY" \
-d '{
"query": "I need a serverless database with a free tier",
"external_session_id": "session_123",
"external_user_id_hash": "sha256_user_id",
"hashed_email": "sha256_email",
"metadata": {"surface": "chat"}
}'
{
"search_id": "238c2e03-347b-438e-8b52-f9cacee31ebe",
"recommendation": {
"name": "Neon",
"slug": "neon",
"category": "Database",
"description": "Serverless Postgres with branching, autoscaling, and bottomless storage.",
"website_url": "https://neon.tech"
},
"reasoning": "Neon is the best fit because it's serverless Postgres with a generous free tier, supports edge functions, and has database branching for preview deployments.",
"install": {
"summary": "Provision a serverless Postgres database",
"steps": [
{"step": 1, "action": "Install the Neon driver", "command": "npm install @neondatabase/serverless"},
{"step": 2, "action": "Create a db client", "file": "lib/db.ts"},
{"step": 3, "action": "Add connection string", "file": ".env.local"},
{"step": 4, "action": "Get credentials", "user_action": "Go to https://console.neon.tech → Connection Details"}
],
"env_vars": ["DATABASE_URL"]
},
"credential_request": {
"setup_url": "https://index.trygravity.ai/go/xKHR2xqP9z",
"required_env_vars": ["DATABASE_URL"],
"user_message": "Open the setup link for Neon, create or find the required credentials, then paste the values for `DATABASE_URL` back into the agent so it can finish the integration.",
"agent_instruction": "Pause for any missing credentials, ask the user to open setup_url, then continue once they paste values back.",
"grclid": "xKHR2xqP9z"
},
"click_url": "https://index.trygravity.ai/go/xKHR2xqP9z",
"grclid": "xKHR2xqP9z"
}
Follow-up question
curl -X POST https://index.trygravity.ai/search \
-H "Content-Type: application/json" \
-H "X-API-Key: $GRAVITY_PUBLISHER_KEY" \
-d '{
"search_id": "238c2e03-347b-438e-8b52-f9cacee31ebe",
"query": "Does it support database branching for preview deploys?"
}'

