Technical reference
API documentation
Description
Authentication
Send the API key in the Authorization header and a unique Idempotency-Key for every write request. Keys are for server-side use only.
Authorization: Bearer <YOUR_API_KEY>
Idempotency-Key: <CLIENT_GENERATED_UUID>
Content-Type: application/jsonDo not send a user UUID. Each API key is already bound to its owner, and every request is authorized as that user. Idempotency-Key is a client-generated UUID that identifies one write attempt, not a user.
AI integration brief
Copy this concise brief into an AI assistant to help it understand the API and generate an integration plan or request example.
Endpoints
Read active categories and use their id values in POST /api/v1/tools.categoryIds.
Call this first and pass one to five returned category UUIDs in categoryIds.
- Required scope
- Public
- Credit cost
- No charge
- Idempotency-Key
- Not required
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| locale | string | No | Optional language code such as en. Defaults to en. |
cURL example
curl 'https://navs.site/api/v1/categories?locale=en'Success response
{
"code": 200,
"msg": "Categories retrieved successfully.",
"data": {
"locale": "en",
"categories": [
{
"id": "<CATEGORY_UUID>",
"slug": "ai-design-tools",
"name": "AI Design Tools"
}
]
},
"timestamp": 1723526400000
}Response fields
| Field | Type | Nullable | Description |
|---|---|---|---|
| data.locale | string | No | Language code used for the returned dictionary. |
| data.categories | array of objects | No | Active category entries with id, slug, and name. Use id in categoryIds. |
Upload each screenshot and optional logo before submitting a tool. The response contains a mediaUploadId for each uploaded image.
Pass each mediaUploadId as screenshots[].mediaId or logo.mediaId. Uploads must belong to the authenticated user.
- Required scope
- tools:write
- Credit cost
- No charge
- Idempotency-Key
- Not required
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| image | file | Yes | Multipart form field. JPEG, PNG, WebP, or GIF; original file up to 5 MB; 10 uploads per user per minute. |
cURL example
curl -X POST https://navs.site/api/v1/media/images \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-F "image=@./preview.png"Success response
{
"code": 201,
"msg": "Image uploaded successfully.",
"data": {
"mediaUploadId": "media_xxx",
"expiresAt": "2026-08-19T05:00:00.000Z"
},
"timestamp": 1723526400000
}Response fields
| Field | Type | Nullable | Description |
|---|---|---|---|
| data.mediaUploadId | string | No | User-owned image ID to use as screenshots[].mediaId or logo.mediaId. |
| data.expiresAt | ISO 8601 timestamp | No | The earliest cleanup time for an image that has not been attached. While the resource still exists it may be attached; after first attachment it remains reusable and can be reused by the same authenticated user. |
Create a private tool draft with validated metadata for manual editorial review; API submissions never publish automatically.
First fetch category IDs with GET /api/v1/categories, then upload one to three screenshots through POST /api/v1/media/images. Use the returned IDs in categoryIds and screenshots[].mediaId. An optional logo also requires its own upload.
- Required scope
- tools:write
- Credit cost
- 1 credit
- Idempotency-Key
- Required
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | 2–160 characters. |
| websiteUrl | string (URL) | Yes | Public HTTP or HTTPS website URL; tracking parameters are removed. |
| oneLineDescription | string | Yes | 2–160 characters. |
| primaryKeyword | string | Yes | 1–25 characters. |
| categoryIds | string[] (UUID) | Yes | Required array with 1–5 UUID strings returned in the id field by GET /api/v1/categories. |
| aiToolType | enum | Yes | core_ai, ai_assisted, or non_ai. |
| platforms | string[] | Yes | 1–8 values: web, ios, android, macos, windows, linux, browser_extension, api. |
| languages | string[] | Yes | 1–20 supported ISO 639-1 language codes, such as en. |
| screenshots | object[] | Yes | Required array with 1–3 items. Each item must contain mediaId (string, required; returned by POST /api/v1/media/images) and title (string, required, 1–120 characters). Do not send url or thumbnailUrl; the server resolves them from mediaId. |
| logo | object | null | No | Optional object. If present, mediaId (string, required; returned by POST /api/v1/media/images) is the only field needed. Send null to omit a logo. |
| features | object[] | Yes | Required array with 1–10 items. Each item must contain title (string, required, 1–120 characters) and description (string, required, 1–500 characters). |
| targetUsers | object[] | No | Optional array with up to 5 items. Each item must contain audience (string, required) and useCase (string, required). Defaults to []. |
| faqItems | object[] | No | Optional array with up to 8 items. Each item must contain question (string, required) and answer (string, required). Defaults to []. |
| pricingModel | enum | Yes | free, freemium, paid, or unknown. |
| trialStatus | enum | Yes | yes, no, or unknown. |
| tagline | string | Yes | 1–250 characters. |
| descriptionMarkdown | string (Markdown) | Yes | 1–2,500 characters. |
| tags | string[] | No | 1–10 tags, each 1–80 characters. Defaults to []. |
| supportEmail | string (email) | No | Valid support email address. |
| youtubeVideoUrl | string (URL) | No | Standard YouTube video URL. |
| faqPageUrl | string (URL) | No | Public HTTP or HTTPS FAQ URL. |
| helpCenterUrl | string (URL) | No | Public HTTP or HTTPS help center URL. |
| priceNote | string | No | Up to 120 characters. |
| pricingDetails | string | No | Up to 5,000 characters. |
| pricingPageUrl | string (URL) | No | Public HTTP or HTTPS pricing URL. |
| alternativeToToolIds | string[] | No | Up to 100 existing tool IDs as positive decimal strings. Defaults to []. |
| submissionNotes | string | No | Up to 2,000 characters. |
cURL example
curl -X POST https://navs.site/api/v1/tools \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-H "Idempotency-Key: <CLIENT_GENERATED_UUID>" \
-H "Content-Type: application/json" \
-d '{
"name": "Example Studio",
"websiteUrl": "https://example.com",
"oneLineDescription": "An AI design workspace for product teams.",
"primaryKeyword": "AI design",
"categoryIds": [
"<CATEGORY_UUID_FROM_CATEGORIES_API>"
],
"aiToolType": "core_ai",
"platforms": [
"web"
],
"languages": [
"en"
],
"screenshots": [
{
"mediaId": "<MEDIA_UPLOAD_ID>",
"title": "Product screenshot"
}
],
"logo": {
"mediaId": "<LOGO_MEDIA_UPLOAD_ID>"
},
"features": [
{
"id": "design",
"title": "AI design",
"description": "Create and revise designs with AI assistance."
}
],
"targetUsers": [],
"faqItems": [],
"pricingModel": "freemium",
"trialStatus": "unknown",
"tagline": "Design together with AI.",
"descriptionMarkdown": "Example Studio helps product teams create and revise visual work.",
"tags": [
"design",
"collaboration"
],
"alternativeToToolIds": []
}'Success response
{
"code": 201,
"msg": "Tool submitted successfully.",
"data": {
"id": "example-com",
"status": "pending"
},
"timestamp": 1723526400000
}Response fields
| Field | Type | Nullable | Description |
|---|---|---|---|
| data.id | string | No | Directory record slug created for the submission. |
| data.status | enum | No | Submission state; API submissions are pending editorial review. |
Response format
Every response uses code (the HTTP status), msg (a human-readable summary), data (business data or null), and timestamp (Unix milliseconds). Failed responses also include errorCode for programmatic handling. The exact data fields are listed under each endpoint.
{
"code": 201,
"msg": "Tool submitted successfully.",
"data": {
"id": "example-com",
"status": "pending"
},
"timestamp": 1723526400000
}Failure response
{
"code": 422,
"msg": "The request failed validation.",
"data": null,
"timestamp": 1723526400000,
"errorCode": "VALIDATION_ERROR"
}Idempotency and retries
Repeating the same key with the same validated request returns the original result without creating another submission or using credits again. A different request with the same key returns a replay-mismatch error.
REST conventions
The API uses HTTPS, resource-oriented paths, HTTP methods, and matching HTTP status codes. Requests and responses use JSON except POST /api/v1/media/images, which accepts multipart/form-data.
Common errors
- 401 UNAUTHORIZED
- The API key is missing, invalid, revoked, or unavailable.
- 403 API_ACCESS_REQUIRED
- The current membership does not include API access.
- 429 RATE_LIMITED
- The key has reached its request limit. Retry after the response delay.
- 402 INSUFFICIENT_CREDITS
- Neither the current membership credits nor purchased credits can cover the operation.
- 422 VALIDATION_ERROR
- The request did not pass validation; no credits are used.
- 409 IDEMPOTENCY_KEY_REPLAY_MISMATCH
- The idempotency key was previously used with a different validated request.
Editorial policy
A successful API request does not promise inclusion, a particular external-link attribute, traffic, or search performance. Content remains subject to editorial review and may be rejected or removed.
Read the Terms of Service