§ Reference
SDKs & client libraries
We don't ship official SDKs — the API is small enough that fifteen lines of native code beats a dependency in every language we've checked. Here are tested snippets you can drop in.
TypeScript / JavaScript
Works in Node 20+, Deno, Bun, browsers (with care about your key).
typescripttypescript
// Node 20+, Deno, Bun, modern browsers
const BASE = "https://livesofthesaintscalendar.com/api/v1";
const KEY = process.env.LOTS_API_KEY!;
async function lots<T>(path: string, init: RequestInit = {}): Promise<T> {
const res = await fetch(BASE + path, {
...init,
headers: {
Authorization: `Bearer ${KEY}`,
Accept: "application/json",
...init.headers,
},
});
if (!res.ok) {
const body = await res.json().catch(() => null);
throw new Error(body?.error?.message ?? res.statusText);
}
return res.json() as Promise<T>;
}
// Usage
const today = await lots("/calendar/today");
const helena = await lots("/saints/saint-helena");Python
pythonpython
import os
import httpx
BASE = "https://livesofthesaintscalendar.com/api/v1"
KEY = os.environ["LOTS_API_KEY"]
class LotsAPIError(Exception): pass
def lots(path: str, **params):
r = httpx.get(BASE + path, params=params, headers={
"Authorization": f"Bearer {KEY}",
"Accept": "application/json",
})
if r.is_error:
msg = r.json().get("error", {}).get("message") if r.content else r.text
raise LotsAPIError(f"{r.status_code} {msg}")
return r.json()
# Usage
today = lots("/calendar/today")
helena = lots("/saints/saint-helena")
patrons = lots("/saints", patronOf="travelers", limit=10)Go
gogo
package lots
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
const Base = "https://livesofthesaintscalendar.com/api/v1"
type Client struct {
Key string
HTTP *http.Client
}
func New() *Client {
return &Client{Key: os.Getenv("LOTS_API_KEY"), HTTP: http.DefaultClient}
}
func (c *Client) Get(path string, query url.Values, out any) error {
u := Base + path
if query != nil { u += "?" + query.Encode() }
req, _ := http.NewRequest("GET", u, nil)
req.Header.Set("Authorization", "Bearer " + c.Key)
res, err := c.HTTP.Do(req)
if err != nil { return err }
defer res.Body.Close()
if res.StatusCode >= 400 {
body, _ := io.ReadAll(res.Body)
return fmt.Errorf("lots: %d %s", res.StatusCode, string(body))
}
return json.NewDecoder(res.Body).Decode(out)
}Ruby
rubyruby
require "net/http"
require "json"
require "uri"
BASE = URI("https://livesofthesaintscalendar.com/api/v1")
KEY = ENV.fetch("LOTS_API_KEY")
def lots(path, **params)
uri = BASE.dup
uri.path = uri.path + path
uri.query = URI.encode_www_form(params) unless params.empty?
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{KEY}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
raise "lots: #{res.code} #{res.body}" unless res.is_a?(Net::HTTPSuccess)
JSON.parse(res.body)
end
today = lots("/calendar/today")
patrons = lots("/saints", patronOf: "travelers")PHP
phpphp
<?php
$base = "https://livesofthesaintscalendar.com/api/v1";
$key = getenv("LOTS_API_KEY");
function lots(string $path, array $params = []): mixed {
global $base, $key;
$url = $base . $path . ($params ? "?" . http_build_query($params) : "");
$ctx = stream_context_create([
"http" => [
"method" => "GET",
"header" => "Authorization: Bearer $key\r\nAccept: application/json\r\n",
],
]);
$body = file_get_contents($url, false, $ctx);
return json_decode($body, true);
}
$today = lots("/calendar/today");
$helena = lots("/saints/saint-helena");Swift
swiftswift
import Foundation
enum LotsError: Error { case http(Int) }
struct Lots {
static let base = URL(string: "https://livesofthesaintscalendar.com/api/v1")!
let key: String
func get<T: Decodable>(_ path: String, as: T.Type) async throws -> T {
var req = URLRequest(url: Lots.base.appending(path: path))
req.setValue("Bearer \(key)", forHTTPHeaderField: "Authorization")
let (data, res) = try await URLSession.shared.data(for: req)
guard let http = res as? HTTPURLResponse, http.statusCode == 200 else {
throw LotsError.http((res as? HTTPURLResponse)?.statusCode ?? 0)
}
return try JSONDecoder().decode(T.self, from: data)
}
}OpenAPI codegen
Want generated bindings? Point any OpenAPI generator at /api/v1/openapi.json — we ship a hand-written 3.1 spec that works with openapi-typescript, orval, Scalar, Stoplight, and the Postman importer.