Code examples
Complete programs you can run as they are. Each one reads your key from FENGSHUI_API_KEY, fetches a Kua number with its favourable directions and a love compatibility score, prints RFC 9457 errors clearly and waits for Retry-After when the quota is used up.
File examples/curl/kua.sh · run with FENGSHUI_API_KEY=fsk_… sh kua.sh
#!/bin/sh
# Kua number and love compatibility with the Feng Shui API.
# Usage: FENGSHUI_API_KEY=fsk_... sh kua.sh (optional: FENGSHUI_API_URL, pipe through `jq` for pretty output)
set -eu
: "${FENGSHUI_API_KEY:?Set FENGSHUI_API_KEY to your API key}"
BASE="${FENGSHUI_API_URL:-https://fengshui-api.com}/api/v2"
# --retry waits for Retry-After on 429; --fail-with-body prints the RFC 9457 error and exits non-zero.
api() {
path="$1"; shift
curl --silent --show-error --fail-with-body --retry 3 \
--header "X-API-Key: $FENGSHUI_API_KEY" \
--get "$BASE$path" "$@"
echo
}
echo "Kua number:"
api /feng-shui/kua --data-urlencode "date=1985-03-15" --data-urlencode "gender=female"
echo "Love compatibility:"
api /compatibility/love --data-urlencode "date1=1990-06-15" --data-urlencode "date2=1992-03-10"
File examples/javascript/kua.mjs · run with FENGSHUI_API_KEY=fsk_… node kua.mjs
// Kua number and love compatibility with the Feng Shui API (Node.js 18+, no dependencies).
// Usage: FENGSHUI_API_KEY=fsk_... node kua.mjs
const BASE = `${process.env.FENGSHUI_API_URL ?? 'https://fengshui-api.com'}/api/v2`;
const KEY = process.env.FENGSHUI_API_KEY;
if (!KEY) {
console.error('Set FENGSHUI_API_KEY to your API key.');
process.exit(1);
}
async function api(path, params, attempt = 1) {
const response = await fetch(`${BASE}${path}?${new URLSearchParams(params)}`, {
headers: { 'X-API-Key': KEY, Accept: 'application/json' },
});
if (response.status === 429 && attempt < 3) {
const seconds = Math.min(Number(response.headers.get('Retry-After') ?? 1), 60);
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
return api(path, params, attempt + 1);
}
const body = await response.json();
if (!response.ok) {
// RFC 9457 problem details; 422 responses list every invalid parameter.
const details = (body.errors ?? []).map((e) => `${e.parameter}: ${e.message}`).join('; ');
throw new Error(`${response.status} ${body.title}: ${details || body.detail}`);
}
return body;
}
const kua = await api('/feng-shui/kua', { date: '1985-03-15', gender: 'female' });
console.log(`Kua ${kua.kua} — ${kua.group} group (${kua.trigram.name})`);
console.log('Favourable directions:');
for (const d of kua.favorableDirections) {
console.log(` ${d.direction.padEnd(3)} ${d.star.padEnd(9)} ${d.meaning}`);
}
const match = await api('/compatibility/love', { date1: '1990-06-15', date2: '1992-03-10' });
console.log(`Love compatibility ${match.first.animal} & ${match.second.animal}: ${match.score}/${match.maxScore} (${match.rating})`);
File examples/python/kua.py · run with FENGSHUI_API_KEY=fsk_… pip install requests && python kua.py
"""Kua number and love compatibility with the Feng Shui API.
Usage: pip install requests && FENGSHUI_API_KEY=fsk_... python kua.py
"""
import os
import sys
import time
import requests
BASE = os.environ.get("FENGSHUI_API_URL", "https://fengshui-api.com") + "/api/v2"
KEY = os.environ.get("FENGSHUI_API_KEY") or sys.exit("Set FENGSHUI_API_KEY to your API key.")
session = requests.Session()
session.headers.update({"X-API-Key": KEY, "Accept": "application/json"})
def api(path: str, **params: str) -> dict:
for attempt in range(3):
response = session.get(BASE + path, params=params, timeout=10)
if response.status_code == 429 and attempt < 2:
time.sleep(min(int(response.headers.get("Retry-After", "1")), 60))
continue
body = response.json()
if not response.ok:
# RFC 9457 problem details; 422 responses list every invalid parameter.
details = "; ".join(f"{e['parameter']}: {e['message']}" for e in body.get("errors", []))
raise RuntimeError(f"{response.status_code} {body['title']}: {details or body.get('detail', '')}")
return body
raise RuntimeError("Rate limit still exceeded.")
kua = api("/feng-shui/kua", date="1985-03-15", gender="female")
print(f"Kua {kua['kua']} — {kua['group']} group ({kua['trigram']['name']})")
print("Favourable directions:")
for d in kua["favorableDirections"]:
print(f" {d['direction']:<3} {d['star']:<9} {d['meaning']}")
match = api("/compatibility/love", date1="1990-06-15", date2="1992-03-10")
print(f"Love compatibility {match['first']['animal']} & {match['second']['animal']}: "
f"{match['score']}/{match['maxScore']} ({match['rating']})")
File examples/php/kua.php · run with FENGSHUI_API_KEY=fsk_… php kua.php
<?php
// Kua number and love compatibility with the Feng Shui API (PHP 8.4+, no dependencies).
// Usage: FENGSHUI_API_KEY=fsk_... php kua.php
$base = (getenv('FENGSHUI_API_URL') ?: 'https://fengshui-api.com').'/api/v2';
$key = getenv('FENGSHUI_API_KEY') ?: exit("Set FENGSHUI_API_KEY to your API key.\n");
function api(string $base, string $key, string $path, array $params): array
{
for ($attempt = 1; ; ++$attempt) {
$context = stream_context_create(['http' => [
'header' => "X-API-Key: $key\r\nAccept: application/json\r\n",
'ignore_errors' => true, // read the body of 4xx responses too
'timeout' => 10,
]]);
$body = file_get_contents($base.$path.'?'.http_build_query($params), false, $context);
$headers = http_get_last_response_headers() ?? [];
preg_match('{HTTP/\S+ (\d{3})}', $headers[0], $match);
$status = (int) $match[1];
if (429 === $status && $attempt < 3) {
$retryAfter = preg_grep('/^Retry-After:/i', $headers);
sleep(min((int) substr((string) reset($retryAfter), 12), 60) ?: 1);
continue;
}
$data = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
if ($status >= 400) {
// RFC 9457 problem details; 422 responses list every invalid parameter.
$details = implode('; ', array_map(fn (array $e) => "{$e['parameter']}: {$e['message']}", $data['errors'] ?? []));
throw new RuntimeException("$status {$data['title']}: ".($details ?: ($data['detail'] ?? '')));
}
return $data;
}
}
$kua = api($base, $key, '/feng-shui/kua', ['date' => '1985-03-15', 'gender' => 'female']);
printf("Kua %d — %s group (%s)\nFavourable directions:\n", $kua['kua'], $kua['group'], $kua['trigram']['name']);
foreach ($kua['favorableDirections'] as $d) {
printf(" %-3s %-9s %s\n", $d['direction'], $d['star'], $d['meaning']);
}
$match = api($base, $key, '/compatibility/love', ['date1' => '1990-06-15', 'date2' => '1992-03-10']);
printf("Love compatibility %s & %s: %d/%d (%s)\n", $match['first']['animal'], $match['second']['animal'], $match['score'], $match['maxScore'], $match['rating']);
File examples/go/main.go · run with FENGSHUI_API_KEY=fsk_… go run main.go
// Kua number and love compatibility with the Feng Shui API (Go standard library only).
// Usage: FENGSHUI_API_KEY=fsk_... go run main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
type problem struct {
Title string `json:"title"`
Detail string `json:"detail"`
Errors []struct {
Parameter string `json:"parameter"`
Message string `json:"message"`
} `json:"errors"`
}
type kuaResult struct {
Kua int `json:"kua"`
Group string `json:"group"`
Trigram struct {
Name string `json:"name"`
} `json:"trigram"`
FavorableDirections []struct {
Direction string `json:"direction"`
Star string `json:"star"`
Meaning string `json:"meaning"`
} `json:"favorableDirections"`
}
type compatibility struct {
First struct{ Animal string } `json:"first"`
Second struct{ Animal string } `json:"second"`
Score int `json:"score"`
MaxScore int `json:"maxScore"`
Rating string `json:"rating"`
}
var (
base = envOr("FENGSHUI_API_URL", "https://fengshui-api.com") + "/api/v2"
key = os.Getenv("FENGSHUI_API_KEY")
client = &http.Client{Timeout: 10 * time.Second}
)
func envOr(name, fallback string) string {
if value := os.Getenv(name); value != "" {
return value
}
return fallback
}
func api(path string, params url.Values, out any) error {
for attempt := 1; ; attempt++ {
request, _ := http.NewRequest(http.MethodGet, base+path+"?"+params.Encode(), nil)
request.Header.Set("X-API-Key", key)
request.Header.Set("Accept", "application/json")
response, err := client.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode == http.StatusTooManyRequests && attempt < 3 {
seconds, _ := strconv.Atoi(response.Header.Get("Retry-After"))
time.Sleep(time.Duration(max(1, min(seconds, 60))) * time.Second)
continue
}
if response.StatusCode >= 400 {
// RFC 9457 problem details; 422 responses list every invalid parameter.
var p problem
_ = json.NewDecoder(response.Body).Decode(&p)
details := make([]string, 0, len(p.Errors))
for _, e := range p.Errors {
details = append(details, e.Parameter+": "+e.Message)
}
if len(details) == 0 {
details = append(details, p.Detail)
}
return fmt.Errorf("%d %s: %s", response.StatusCode, p.Title, strings.Join(details, "; "))
}
return json.NewDecoder(response.Body).Decode(out)
}
}
func main() {
if key == "" {
fmt.Fprintln(os.Stderr, "Set FENGSHUI_API_KEY to your API key.")
os.Exit(1)
}
var kua kuaResult
if err := api("/feng-shui/kua", url.Values{"date": {"1985-03-15"}, "gender": {"female"}}, &kua); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("Kua %d — %s group (%s)\nFavourable directions:\n", kua.Kua, kua.Group, kua.Trigram.Name)
for _, d := range kua.FavorableDirections {
fmt.Printf(" %-3s %-9s %s\n", d.Direction, d.Star, d.Meaning)
}
var match compatibility
if err := api("/compatibility/love", url.Values{"date1": {"1990-06-15"}, "date2": {"1992-03-10"}}, &match); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("Love compatibility %s & %s: %d/%d (%s)\n", match.First.Animal, match.Second.Animal, match.Score, match.MaxScore, match.Rating)
}
File examples/java/Kua.java · run with FENGSHUI_API_KEY=fsk_… java Kua.java
// Kua number and love compatibility with the Feng Shui API (Java 17+, no dependencies).
// Usage: FENGSHUI_API_KEY=fsk_... java Kua.java
// For brevity the JSON fields are read with a small helper; in an application map responses with Jackson or Gson.
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class Kua {
private static final String BASE = System.getenv().getOrDefault("FENGSHUI_API_URL", "https://fengshui-api.com") + "/api/v2";
private static final String KEY = System.getenv("FENGSHUI_API_KEY");
private static final HttpClient CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
static String api(String path, Map<String, String> params) throws Exception {
String query = params.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpRequest request = HttpRequest.newBuilder(URI.create(BASE + path + "?" + query))
.header("X-API-Key", KEY)
.header("Accept", "application/json")
.timeout(Duration.ofSeconds(10))
.build();
for (int attempt = 1; ; attempt++) {
HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 429 && attempt < 3) {
long seconds = response.headers().firstValueAsLong("Retry-After").orElse(1);
Thread.sleep(Math.min(seconds, 60) * 1000);
continue;
}
if (response.statusCode() >= 400) {
// RFC 9457 problem details
throw new IllegalStateException(response.statusCode() + " " + field(response.body(), "title") + ": " + response.body());
}
return response.body();
}
}
/** Value of the first scalar JSON field with this name. */
static String field(String json, String name) {
Matcher m = Pattern.compile("\"" + name + "\":\\s*(\"((?:[^\"\\\\]|\\\\.)*)\"|[^,}\\]]+)").matcher(json);
return m.find() ? (m.group(2) != null ? m.group(2) : m.group(1).trim()) : "";
}
public static void main(String[] args) throws Exception {
if (KEY == null || KEY.isBlank()) {
System.err.println("Set FENGSHUI_API_KEY to your API key.");
System.exit(1);
}
Map<String, String> kuaParams = new LinkedHashMap<>();
kuaParams.put("date", "1985-03-15");
kuaParams.put("gender", "female");
String kua = api("/feng-shui/kua", kuaParams);
System.out.printf("Kua %s — %s group%n", field(kua, "kua"), field(kua, "group"));
String favourable = kua.substring(kua.indexOf("\"favorableDirections\""), kua.indexOf("\"unfavorableDirections\""));
Matcher direction = Pattern.compile("\"direction\":\"(\\w+)\",\"star\":\"([^\"]+)\"").matcher(favourable);
System.out.println("Favourable directions:");
while (direction.find()) {
System.out.printf(" %-3s %s%n", direction.group(1), direction.group(2));
}
Map<String, String> loveParams = new LinkedHashMap<>();
loveParams.put("date1", "1990-06-15");
loveParams.put("date2", "1992-03-10");
String match = api("/compatibility/love", loveParams);
System.out.printf("Love compatibility: %s/%s (%s)%n", field(match, "score"), field(match, "maxScore"), field(match, "rating"));
}
}
File examples/csharp/Program.cs · run with FENGSHUI_API_KEY=fsk_… dotnet run
// Kua number and love compatibility with the Feng Shui API (.NET 8, no extra packages).
// Usage: FENGSHUI_API_KEY=fsk_... dotnet run (FengShuiExample.csproj is next to this file)
using System.Net;
using System.Text.Json;
var baseUrl = (Environment.GetEnvironmentVariable("FENGSHUI_API_URL") ?? "https://fengshui-api.com") + "/api/v2";
var key = Environment.GetEnvironmentVariable("FENGSHUI_API_KEY");
if (string.IsNullOrEmpty(key))
{
Console.Error.WriteLine("Set FENGSHUI_API_KEY to your API key.");
return 1;
}
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
http.DefaultRequestHeaders.Add("X-API-Key", key);
http.DefaultRequestHeaders.Add("Accept", "application/json");
async Task<JsonElement> Api(string path, Dictionary<string, string> parameters)
{
var query = string.Join("&", parameters.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"));
for (var attempt = 1; ; attempt++)
{
using var response = await http.GetAsync($"{baseUrl}{path}?{query}");
if (response.StatusCode == HttpStatusCode.TooManyRequests && attempt < 3)
{
var seconds = response.Headers.RetryAfter?.Delta?.TotalSeconds ?? 1;
await Task.Delay(TimeSpan.FromSeconds(Math.Min(seconds, 60)));
continue;
}
var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
if (!response.IsSuccessStatusCode)
{
// RFC 9457 problem details; 422 responses list every invalid parameter.
var details = body.TryGetProperty("errors", out var errors)
? string.Join("; ", errors.EnumerateArray().Select(e => $"{e.GetProperty("parameter")}: {e.GetProperty("message")}"))
: body.GetProperty("detail").GetString();
throw new HttpRequestException($"{(int)response.StatusCode} {body.GetProperty("title")}: {details}");
}
return body;
}
}
var kua = await Api("/feng-shui/kua", new() { ["date"] = "1985-03-15", ["gender"] = "female" });
Console.WriteLine($"Kua {kua.GetProperty("kua")} — {kua.GetProperty("group")} group ({kua.GetProperty("trigram").GetProperty("name")})");
Console.WriteLine("Favourable directions:");
foreach (var d in kua.GetProperty("favorableDirections").EnumerateArray())
{
Console.WriteLine($" {d.GetProperty("direction"),-3} {d.GetProperty("star"),-9} {d.GetProperty("meaning")}");
}
var match = await Api("/compatibility/love", new() { ["date1"] = "1990-06-15", ["date2"] = "1992-03-10" });
Console.WriteLine($"Love compatibility {match.GetProperty("first").GetProperty("animal")} & {match.GetProperty("second").GetProperty("animal")}: " +
$"{match.GetProperty("score")}/{match.GetProperty("maxScore")} ({match.GetProperty("rating")})");
return 0;
File examples/ruby/kua.rb · run with FENGSHUI_API_KEY=fsk_… ruby kua.rb
# Kua number and love compatibility with the Feng Shui API (Ruby standard library only).
# Usage: FENGSHUI_API_KEY=fsk_... ruby kua.rb
require 'json'
require 'net/http'
require 'uri'
BASE = "#{ENV.fetch('FENGSHUI_API_URL', 'https://fengshui-api.com')}/api/v2"
KEY = ENV['FENGSHUI_API_KEY'] || abort('Set FENGSHUI_API_KEY to your API key.')
def api(path, params)
uri = URI("#{BASE}#{path}")
uri.query = URI.encode_www_form(params)
(1..3).each do |attempt|
request = Net::HTTP::Get.new(uri, 'X-API-Key' => KEY, 'Accept' => 'application/json')
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', read_timeout: 10) { |http| http.request(request) }
if response.code == '429' && attempt < 3
sleep([response['Retry-After'].to_i, 1].max.clamp(1, 60))
next
end
body = JSON.parse(response.body)
unless response.is_a?(Net::HTTPSuccess)
# RFC 9457 problem details; 422 responses list every invalid parameter.
details = (body['errors'] || []).map { |e| "#{e['parameter']}: #{e['message']}" }.join('; ')
raise "#{response.code} #{body['title']}: #{details.empty? ? body['detail'] : details}"
end
return body
end
end
kua = api('/feng-shui/kua', date: '1985-03-15', gender: 'female')
puts "Kua #{kua['kua']} — #{kua['group']} group (#{kua['trigram']['name']})"
puts 'Favourable directions:'
kua['favorableDirections'].each do |d|
puts format(' %-3s %-9s %s', d['direction'], d['star'], d['meaning'])
end
match = api('/compatibility/love', date1: '1990-06-15', date2: '1992-03-10')
puts "Love compatibility #{match['first']['animal']} & #{match['second']['animal']}: #{match['score']}/#{match['maxScore']} (#{match['rating']})"
Expected output
Kua 9 — East group (Li) Favourable directions: E Sheng Qi Success, wealth and vitality — the best direction SE Tian Yi Health, healing and helpful people N Yan Nian Longevity, harmony and good relationships S Fu Wei Stability, clarity and personal growth Love compatibility Horse & Monkey: 3/4 (good)
Postman, Insomnia, Bruno
No collection to maintain: import openapi.yaml (or openapi.json) — every endpoint, parameter and example appears as a ready request. Set the X-API-Key header once in the collection's authorisation settings.
Generate a typed client
The OpenAPI 3.1 contract works with standard generators, for example:
# TypeScript types npx openapi-typescript https://fengshui-api.com/openapi.yaml -o fengshui-api.d.ts # A client in almost any language (Java, Kotlin, C#, Go, Python, Swift…) docker run --rm -v "$PWD:/out" openapitools/openapi-generator-cli generate \ -i https://fengshui-api.com/openapi.yaml -g python -o /out/fengshui-client
Tips for production
- Keep the key on your server. Browser code would expose it to everyone; call the API from your backend and cache the results — they never change for the same input.
- Send the key in the
X-API-Keyheader (orAuthorization: Bearer), never in the URL. - Watch the
RateLimitheader (r=remaining requests) and back off on429usingRetry-After. - Show
errors[].messagefrom 422 responses directly to users — they are written for people.
Per-endpoint parameters and responses: API reference. No key yet? Create a free account.