Save Bookmark
curl --request POST \
--url https://api.dexi.net/api/v1/bookmarks/save \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "<string>",
"title": "<string>",
"selection": "<string>",
"comment": "<string>",
"description": "<string>",
"append": true
}
'import requests
url = "https://api.dexi.net/api/v1/bookmarks/save"
payload = {
"url": "<string>",
"title": "<string>",
"selection": "<string>",
"comment": "<string>",
"description": "<string>",
"append": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: '<string>',
title: '<string>',
selection: '<string>',
comment: '<string>',
description: '<string>',
append: true
})
};
fetch('https://api.dexi.net/api/v1/bookmarks/save', 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://api.dexi.net/api/v1/bookmarks/save",
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([
'url' => '<string>',
'title' => '<string>',
'selection' => '<string>',
'comment' => '<string>',
'description' => '<string>',
'append' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.dexi.net/api/v1/bookmarks/save"
payload := strings.NewReader("{\n \"url\": \"<string>\",\n \"title\": \"<string>\",\n \"selection\": \"<string>\",\n \"comment\": \"<string>\",\n \"description\": \"<string>\",\n \"append\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://api.dexi.net/api/v1/bookmarks/save")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"<string>\",\n \"title\": \"<string>\",\n \"selection\": \"<string>\",\n \"comment\": \"<string>\",\n \"description\": \"<string>\",\n \"append\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dexi.net/api/v1/bookmarks/save")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"<string>\",\n \"title\": \"<string>\",\n \"selection\": \"<string>\",\n \"comment\": \"<string>\",\n \"description\": \"<string>\",\n \"append\": true\n}"
response = http.request(request)
puts response.read_bodyCapture
Save Bookmark
Save a page into your Dexi workspace
POST
/
api
/
v1
/
bookmarks
/
save
Save Bookmark
curl --request POST \
--url https://api.dexi.net/api/v1/bookmarks/save \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "<string>",
"title": "<string>",
"selection": "<string>",
"comment": "<string>",
"description": "<string>",
"append": true
}
'import requests
url = "https://api.dexi.net/api/v1/bookmarks/save"
payload = {
"url": "<string>",
"title": "<string>",
"selection": "<string>",
"comment": "<string>",
"description": "<string>",
"append": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: '<string>',
title: '<string>',
selection: '<string>',
comment: '<string>',
description: '<string>',
append: true
})
};
fetch('https://api.dexi.net/api/v1/bookmarks/save', 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://api.dexi.net/api/v1/bookmarks/save",
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([
'url' => '<string>',
'title' => '<string>',
'selection' => '<string>',
'comment' => '<string>',
'description' => '<string>',
'append' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.dexi.net/api/v1/bookmarks/save"
payload := strings.NewReader("{\n \"url\": \"<string>\",\n \"title\": \"<string>\",\n \"selection\": \"<string>\",\n \"comment\": \"<string>\",\n \"description\": \"<string>\",\n \"append\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://api.dexi.net/api/v1/bookmarks/save")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"<string>\",\n \"title\": \"<string>\",\n \"selection\": \"<string>\",\n \"comment\": \"<string>\",\n \"description\": \"<string>\",\n \"append\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dexi.net/api/v1/bookmarks/save")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"<string>\",\n \"title\": \"<string>\",\n \"selection\": \"<string>\",\n \"comment\": \"<string>\",\n \"description\": \"<string>\",\n \"append\": true\n}"
response = http.request(request)
puts response.read_bodyThe one endpoint personal API tokens authenticate — the write-only capture path used by the iOS share-sheet Shortcut. The saved page becomes a bookmark-backed note: searchable (full-text and semantic), taggable, and connected like everything else in your workspace.
Saving a new URL creates a bookmark and its note. Saving a URL already in your workspace doesn’t duplicate it — the existing note is reused, and any
string
required
The page URL. Must start with
http:// or https:// — anything else is rejected with 422.string
Page title. Optional; Dexi falls back to the URL when absent.
string
Selected text to save with the page — added to the note body as a quoted highlight (
> -prefixed lines, rendered as a blockquote).string
Optional note on the highlight — lands as the paragraph directly below the quoted
selection. Hashtags typed here become tags on the note.string
The page’s meta description, if you have it. On a first save it seeds the note body (above the source line) as an editable starting point; values longer than 500 characters are truncated. Ignored when the URL is already in your workspace. When omitted, Dexi scrapes the description from the page in the background instead.
boolean
default:"false"
Explicitly request append behavior when the URL is already saved.
selection is appended as another quoted highlight (so repeatedly sharing or highlighting the same article accumulates your annotations on one note).
Example request
curl -X POST https://api.dexi.net/api/v1/bookmarks/save \
-H "Authorization: Bearer dxi_2f7c1a9e4b8d3f6a0c5e9b2d7f4a8c1e3b6d9f2a" \
-H "Content-Type: application/json" \
-d '{
"url": "https://arxiv.org/abs/1706.03762",
"title": "Attention Is All You Need",
"selection": "The dominant sequence transduction models are based on complex recurrent networks.",
"comment": "The opening line of the transformer paper #ml"
}'
Example response
{
"bookmark": {
"id": "7c1f3e58-2ab4-4b6f-9d21-8f4e5c6a7b90",
"note_id": "2982e9b4-19fe-40fd-9a69-e23d45e53b13",
"title": "Attention Is All You Need",
"url": "https://arxiv.org/abs/1706.03762",
"icon": "https://api.dexi.net/media/icons/arxiv.org.png",
"created": "2026-08-05T14:20:00+00:00",
"updated": "2026-08-05T14:20:00+00:00"
},
"note_id": "2982e9b4-19fe-40fd-9a69-e23d45e53b13",
"is_new_bookmark": true,
"selection_appended": false
}
icon is null until the favicon fetch completes in the background.
Errors
| Status | Meaning |
|---|---|
401 | Missing, revoked, or malformed token |
402 | Free-plan note limit reached — machine-readable body below |
422 | Invalid body (e.g. non-http(s) url) |
402 response
{
"detail": {
"code": "note_limit",
"message": "You've reached the free plan limit of 1,000 notes.",
"used": 1000,
"limit": 1000
}
}
iOS Shortcut
The share-sheet Shortcut is a single Get Contents of URL action posting this shape with your token in theAuthorization header. Step-by-step setup: Automate capture with API tokens.