← Anagram Solver / Data API free · no run API

Anagram Solver data API

Everything the app itself does with the answers you keep, you can do from a script.

There is no run API here, and that is not an omission

Most SkillSafe apps put a language model behind the platform's metered job endpoint, and this page would normally document it. Anagram Solver has no model. The whole search is a generator over typed arrays in your browser, reading four text files, so there is nothing to submit a job to and nothing to bill. The metered path is deliberately not printed anywhere in this bundle: the free label is granted by scanning an app's own source for paid-API usage, and a documentation sentence is not worth risking it over.

That is enforced rather than merely true. Anagram Solver holds the platform's Completely free label, and while an app holds it the paid job endpoint returns 403 for that app and data-API usage is not metered to users. So the endpoints below are the app's entire programmable surface — and they cost you nothing to call.

Base URL and envelope

All endpoints live under:

https://api.skillsafe.ai/v1/app-api

Every response is a JSON envelope. Success carries data; failure carries error with a stable code. Check for error first — an HTTP 200 with an error body is possible on partial failures.

{"ok":true,"data":{ … }}
{"ok":false,"error":{"code":"NOT_FOUND","message":"no such record"}}
CodeHTTPWhat it means here
UNAUTHORIZED401Missing or malformed bearer token.
FORBIDDEN403Valid token, wrong app, or the metered job endpoint on a free app.
NOT_FOUND404No such record visible to you. Rows are scoped to their owner, so another subject's record is a 404, never a 403.
VALIDATION_ERROR400A declared field has the wrong type. The usual cause is a timestamp that is not ISO-8601 with a Z.
RATE_LIMITED429120 requests a minute on the data endpoints, 30 on /similar. Free apps also have a daily ceiling of 5,000 vector operations.
QUOTA_EXCEEDED4091,000 records per owner, or 64 KB in one document.

The finds collection

One declared collection, holding the answers a person chose to keep. acl_read is owner and acl_write is user, so every row belongs to the subject that created it and nobody else can read it.

FieldTypeNotes
phrasestringWhat was searched. Embedded for meaning search.
answerstringThe anagram itself, words separated by spaces. Embedded.
notestringFree text from the person keeping it. Embedded.
word_countnumberWords in the answer.
lettersnumberLetters in the phrase, ignoring everything else.
saved_attimestampISO-8601 with a Z only. Epoch numbers are rejected.

embed is ["phrase", "answer", "note"] — the three fields /similar searches over. There is no backfill: a record written before a field was embedded stays unsearchable until it is rewritten.

Steps

Pick a language once and every sample on the page follows it. The choice is remembered. Replace YOUR_TOKEN with the string from the session page, or mint a fresh one in step 1.

1 Get a token

A guest token is minted without any credentials and owns its own kept answers. If you would rather use the identity your browser already has, take the token from the session page instead — every later step is identical.

POST https://api.skillsafe.ai/v1/app-api/guest
curl -s -X POST https://api.skillsafe.ai/v1/app-api/guest
import json, urllib.request

TOKEN = "YOUR_TOKEN"   # from /tokens.html, or step 1
req = urllib.request.Request(
    "https://api.skillsafe.ai/v1/app-api/guest",
    method="POST",
    )
with urllib.request.urlopen(req) as r:
    print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";   // from /tokens.html, or step 1

const res = await fetch("https://api.skillsafe.ai/v1/app-api/guest", {
  method: "POST",
});
const { data, error } = await res.json();
if (error) throw new Error(error.code);
console.log(data);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	token := "YOUR_TOKEN" // from /tokens.html, or step 1
	req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/guest", nil)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String token = "YOUR_TOKEN";   // from /tokens.html, or step 1
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.skillsafe.ai/v1/app-api/guest"))
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
require "json"
require "net/http"

token = "YOUR_TOKEN"   # from /tokens.html, or step 1
uri = URI("https://api.skillsafe.ai/v1/app-api/guest")
req = Net::HTTP::Post.new(uri)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";   // from /tokens.html, or step 1

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.skillsafe.ai/v1/app-api/guest");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json"]);
$res = curl_exec($ch);
curl_close($ch);
print_r(json_decode($res, true)["data"]);
using System.Net.Http;
using System.Text;

var token = "YOUR_TOKEN";   // from /tokens.html, or step 1
using var client = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/guest");
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());

Returns:

{"data":{"token":"aut_…","subject_type":"guest","subject_id":"gst_…"}}

2 See who the token is

Three fields, and there are deliberately no others: no email, no display name, no id beyond the subject id. The signed-in test is subject_type == "user". credits is your wallet, not a charge for anything on this app.

GET https://api.skillsafe.ai/v1/app-api/me
curl -s -X GET https://api.skillsafe.ai/v1/app-api/me \
  -H "Authorization: Bearer $ANAGRAM_SOLVER_TOKEN"
import json, urllib.request

TOKEN = "YOUR_TOKEN"   # from /tokens.html, or step 1
req = urllib.request.Request(
    "https://api.skillsafe.ai/v1/app-api/me",
    method="GET",
    headers={"Authorization": "Bearer " + TOKEN},)
with urllib.request.urlopen(req) as r:
    print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";   // from /tokens.html, or step 1

const res = await fetch("https://api.skillsafe.ai/v1/app-api/me", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
  },
});
const { data, error } = await res.json();
if (error) throw new Error(error.code);
console.log(data);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	token := "YOUR_TOKEN" // from /tokens.html, or step 1
	req, _ := http.NewRequest("GET", "https://api.skillsafe.ai/v1/app-api/me", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String token = "YOUR_TOKEN";   // from /tokens.html, or step 1
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.skillsafe.ai/v1/app-api/me"))
    .header("Authorization", "Bearer " + token)
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
require "json"
require "net/http"

token = "YOUR_TOKEN"   # from /tokens.html, or step 1
uri = URI("https://api.skillsafe.ai/v1/app-api/me")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{token}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";   // from /tokens.html, or step 1

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.skillsafe.ai/v1/app-api/me");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json", "Authorization: Bearer $token"]);
$res = curl_exec($ch);
curl_close($ch);
print_r(json_decode($res, true)["data"]);
using System.Net.Http;
using System.Text;

var token = "YOUR_TOKEN";   // from /tokens.html, or step 1
using var client = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("GET"), "https://api.skillsafe.ai/v1/app-api/me");
req.Headers.Add("Authorization", "Bearer " + token);
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());

Returns:

{"data":{"subject_type":"guest","subject_id":"gst_…","credits":0}}

3 Keep an answer

saved_at is a declared timestamp field, and it accepts only ISO-8601 with a Z. Epoch milliseconds, epoch seconds and a naive ISO string are all rejected with Field type mismatch. Undeclared keys are stored and returned intact, they are simply not filterable.

POST https://api.skillsafe.ai/v1/app-api/collections/finds/records
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/finds/records \
  -H "Authorization: Bearer $ANAGRAM_SOLVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"phrase":"schoolmaster","answer":"the classroom","note":"for the pub quiz","word_count":2,"letters":12,"saved_at":"2026-08-26T12:00:00Z"}'
import json, urllib.request

TOKEN = "YOUR_TOKEN"   # from /tokens.html, or step 1
body = json.dumps({"phrase":"schoolmaster","answer":"the classroom","note":"for the pub quiz","word_count":2,"letters":12,"saved_at":"2026-08-26T12:00:00Z"}).encode()
req = urllib.request.Request(
    "https://api.skillsafe.ai/v1/app-api/collections/finds/records",
    data=body,
    method="POST",
    headers={"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"},)
with urllib.request.urlopen(req) as r:
    print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";   // from /tokens.html, or step 1

const res = await fetch("https://api.skillsafe.ai/v1/app-api/collections/finds/records", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"phrase":"schoolmaster","answer":"the classroom","note":"for the pub quiz","word_count":2,"letters":12,"saved_at":"2026-08-26T12:00:00Z"}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code);
console.log(data);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	token := "YOUR_TOKEN" // from /tokens.html, or step 1
	body := []byte(`{"phrase":"schoolmaster","answer":"the classroom","note":"for the pub quiz","word_count":2,"letters":12,"saved_at":"2026-08-26T12:00:00Z"}`)
	req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/collections/finds/records", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String token = "YOUR_TOKEN";   // from /tokens.html, or step 1
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.skillsafe.ai/v1/app-api/collections/finds/records"))
    .header("Authorization", "Bearer " + token)
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString(
        """
{"phrase":"schoolmaster","answer":"the classroom","note":"for the pub quiz","word_count":2,"letters":12,"saved_at":"2026-08-26T12:00:00Z"}"""))
    .build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
require "json"
require "net/http"

token = "YOUR_TOKEN"   # from /tokens.html, or step 1
uri = URI("https://api.skillsafe.ai/v1/app-api/collections/finds/records")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = { "phrase" =>"schoolmaster","answer" =>"the classroom","note" =>"for the pub quiz","word_count" =>2,"letters" =>12,"saved_at" =>"2026-08-26T12:00:00Z"}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";   // from /tokens.html, or step 1

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.skillsafe.ai/v1/app-api/collections/finds/records");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json", "Authorization: Bearer $token", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"phrase":"schoolmaster","answer":"the classroom","note":"for the pub quiz","word_count":2,"letters":12,"saved_at":"2026-08-26T12:00:00Z"}');
$res = curl_exec($ch);
curl_close($ch);
print_r(json_decode($res, true)["data"]);
using System.Net.Http;
using System.Text;

var token = "YOUR_TOKEN";   // from /tokens.html, or step 1
using var client = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/collections/finds/records");
req.Headers.Add("Authorization", "Bearer " + token);
req.Content = new StringContent(@"{""phrase"":""schoolmaster"",""answer"":""the classroom"",""note"":""for the pub quiz"",""word_count"":2,""letters"":12,""saved_at"":""2026-08-26T12:00:00Z""}",
    Encoding.UTF8, "application/json");
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());

Returns:

{"data":{"record_id":"rec_…"}}

4 List what you have kept

Every where entry must be an operator object — the bare-value shorthand {"word_count": 2} is rejected. Operators are eq ne lt lte gt gte in contains. The sort key is sort, an object; order_by is silently ignored and the query quietly falls back to newest-first.

POST https://api.skillsafe.ai/v1/app-api/collections/finds/query
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/finds/query \
  -H "Authorization: Bearer $ANAGRAM_SOLVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"where":{"word_count":{"lte":2}},"sort":{"field":"saved_at","dir":"desc"},"limit":20}'
import json, urllib.request

TOKEN = "YOUR_TOKEN"   # from /tokens.html, or step 1
body = json.dumps({"where":{"word_count":{"lte":2}},"sort":{"field":"saved_at","dir":"desc"},"limit":20}).encode()
req = urllib.request.Request(
    "https://api.skillsafe.ai/v1/app-api/collections/finds/query",
    data=body,
    method="POST",
    headers={"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"},)
with urllib.request.urlopen(req) as r:
    print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";   // from /tokens.html, or step 1

const res = await fetch("https://api.skillsafe.ai/v1/app-api/collections/finds/query", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"where":{"word_count":{"lte":2}},"sort":{"field":"saved_at","dir":"desc"},"limit":20}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code);
console.log(data);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	token := "YOUR_TOKEN" // from /tokens.html, or step 1
	body := []byte(`{"where":{"word_count":{"lte":2}},"sort":{"field":"saved_at","dir":"desc"},"limit":20}`)
	req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/collections/finds/query", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String token = "YOUR_TOKEN";   // from /tokens.html, or step 1
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.skillsafe.ai/v1/app-api/collections/finds/query"))
    .header("Authorization", "Bearer " + token)
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString(
        """
{"where":{"word_count":{"lte":2}},"sort":{"field":"saved_at","dir":"desc"},"limit":20}"""))
    .build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
require "json"
require "net/http"

token = "YOUR_TOKEN"   # from /tokens.html, or step 1
uri = URI("https://api.skillsafe.ai/v1/app-api/collections/finds/query")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = { "where" =>{ "word_count" =>{ "lte" =>2}},"sort" =>{ "field" =>"saved_at","dir" =>"desc"},"limit" =>20}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";   // from /tokens.html, or step 1

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.skillsafe.ai/v1/app-api/collections/finds/query");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json", "Authorization: Bearer $token", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"where":{"word_count":{"lte":2}},"sort":{"field":"saved_at","dir":"desc"},"limit":20}');
$res = curl_exec($ch);
curl_close($ch);
print_r(json_decode($res, true)["data"]);
using System.Net.Http;
using System.Text;

var token = "YOUR_TOKEN";   // from /tokens.html, or step 1
using var client = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/collections/finds/query");
req.Headers.Add("Authorization", "Bearer " + token);
req.Content = new StringContent(@"{""where"":{""word_count"":{""lte"":2}},""sort"":{""field"":""saved_at"",""dir"":""desc""},""limit"":20}",
    Encoding.UTF8, "application/json");
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());

Returns:

{"data":{"records":[{"record_id":"rec_…","doc":{"phrase":"schoolmaster","answer":"the classroom","word_count":2}}],"next_cursor":null}}

5 Search them by meaning

Vector search over phrase, answer and note, the three fields declared in embed. Note the envelope: this endpoint returns the records array itself where /query wraps it in an object. Rate limited to 30 requests a minute per IP, and indexing is asynchronous, so a search immediately after a write can lag by a second or two.

POST https://api.skillsafe.ai/v1/app-api/collections/finds/similar
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/finds/similar \
  -H "Authorization: Bearer $ANAGRAM_SOLVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text":"the one about a classroom","limit":8}'
import json, urllib.request

TOKEN = "YOUR_TOKEN"   # from /tokens.html, or step 1
body = json.dumps({"text":"the one about a classroom","limit":8}).encode()
req = urllib.request.Request(
    "https://api.skillsafe.ai/v1/app-api/collections/finds/similar",
    data=body,
    method="POST",
    headers={"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"},)
with urllib.request.urlopen(req) as r:
    print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";   // from /tokens.html, or step 1

const res = await fetch("https://api.skillsafe.ai/v1/app-api/collections/finds/similar", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"text":"the one about a classroom","limit":8}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code);
console.log(data);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	token := "YOUR_TOKEN" // from /tokens.html, or step 1
	body := []byte(`{"text":"the one about a classroom","limit":8}`)
	req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/collections/finds/similar", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String token = "YOUR_TOKEN";   // from /tokens.html, or step 1
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.skillsafe.ai/v1/app-api/collections/finds/similar"))
    .header("Authorization", "Bearer " + token)
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString(
        """
{"text":"the one about a classroom","limit":8}"""))
    .build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
require "json"
require "net/http"

token = "YOUR_TOKEN"   # from /tokens.html, or step 1
uri = URI("https://api.skillsafe.ai/v1/app-api/collections/finds/similar")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = { "text" =>"the one about a classroom","limit" =>8}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";   // from /tokens.html, or step 1

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.skillsafe.ai/v1/app-api/collections/finds/similar");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json", "Authorization: Bearer $token", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"text":"the one about a classroom","limit":8}');
$res = curl_exec($ch);
curl_close($ch);
print_r(json_decode($res, true)["data"]);
using System.Net.Http;
using System.Text;

var token = "YOUR_TOKEN";   // from /tokens.html, or step 1
using var client = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/collections/finds/similar");
req.Headers.Add("Authorization", "Bearer " + token);
req.Content = new StringContent(@"{""text"":""the one about a classroom"",""limit"":8}",
    Encoding.UTF8, "application/json");
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());

Returns:

{"data":[{"record_id":"rec_…","score":0.71,"doc":{"answer":"the classroom"}}]}

6 Delete one

Substitute the record_id the create step returned. Deleting a record you do not own returns 404 rather than 403, because acl_read: "owner" means rows belonging to another subject are not visible to you at all.

DELETE https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME
curl -s -X DELETE https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME \
  -H "Authorization: Bearer $ANAGRAM_SOLVER_TOKEN"
import json, urllib.request

TOKEN = "YOUR_TOKEN"   # from /tokens.html, or step 1
req = urllib.request.Request(
    "https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME",
    method="DELETE",
    headers={"Authorization": "Bearer " + TOKEN},)
with urllib.request.urlopen(req) as r:
    print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";   // from /tokens.html, or step 1

const res = await fetch("https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
  },
});
const { data, error } = await res.json();
if (error) throw new Error(error.code);
console.log(data);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	token := "YOUR_TOKEN" // from /tokens.html, or step 1
	req, _ := http.NewRequest("DELETE", "https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String token = "YOUR_TOKEN";   // from /tokens.html, or step 1
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME"))
    .header("Authorization", "Bearer " + token)
    .method("DELETE", HttpRequest.BodyPublishers.noBody())
    .build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
require "json"
require "net/http"

token = "YOUR_TOKEN"   # from /tokens.html, or step 1
uri = URI("https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer #{token}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";   // from /tokens.html, or step 1

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json", "Authorization: Bearer $token"]);
$res = curl_exec($ch);
curl_close($ch);
print_r(json_decode($res, true)["data"]);
using System.Net.Http;
using System.Text;

var token = "YOUR_TOKEN";   // from /tokens.html, or step 1
using var client = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("DELETE"), "https://api.skillsafe.ai/v1/app-api/collections/finds/records/rec_REPLACE_ME");
req.Headers.Add("Authorization", "Bearer " + token);
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());

Returns:

{"data":{"deleted":true}}

7 Check your usage

What the app's own “what it is costing” panel reads. Quotas that matter here: 10,000 records per collection, 1,000 records per owner, 64 KB per document. A completely-free app also gets a daily ceiling of 5,000 vector operations, and this is where the remaining allowance is reported.

GET https://api.skillsafe.ai/v1/app-api/storage
curl -s -X GET https://api.skillsafe.ai/v1/app-api/storage \
  -H "Authorization: Bearer $ANAGRAM_SOLVER_TOKEN"
import json, urllib.request

TOKEN = "YOUR_TOKEN"   # from /tokens.html, or step 1
req = urllib.request.Request(
    "https://api.skillsafe.ai/v1/app-api/storage",
    method="GET",
    headers={"Authorization": "Bearer " + TOKEN},)
with urllib.request.urlopen(req) as r:
    print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";   // from /tokens.html, or step 1

const res = await fetch("https://api.skillsafe.ai/v1/app-api/storage", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
  },
});
const { data, error } = await res.json();
if (error) throw new Error(error.code);
console.log(data);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	token := "YOUR_TOKEN" // from /tokens.html, or step 1
	req, _ := http.NewRequest("GET", "https://api.skillsafe.ai/v1/app-api/storage", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String token = "YOUR_TOKEN";   // from /tokens.html, or step 1
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.skillsafe.ai/v1/app-api/storage"))
    .header("Authorization", "Bearer " + token)
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
require "json"
require "net/http"

token = "YOUR_TOKEN"   # from /tokens.html, or step 1
uri = URI("https://api.skillsafe.ai/v1/app-api/storage")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{token}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";   // from /tokens.html, or step 1

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.skillsafe.ai/v1/app-api/storage");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json", "Authorization: Bearer $token"]);
$res = curl_exec($ch);
curl_close($ch);
print_r(json_decode($res, true)["data"]);
using System.Net.Http;
using System.Text;

var token = "YOUR_TOKEN";   // from /tokens.html, or step 1
using var client = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("GET"), "https://api.skillsafe.ai/v1/app-api/storage");
req.Headers.Add("Authorization", "Bearer " + token);
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());

Returns:

{"data":{"records":{"used":3,"limit":1000},"vector_ops":{"daily_ops_remaining":4997}}}