Happy Decoder API

Бесплатный API для расшифровки зашифрованных VPN-подписок Happ

API v1 · обновлено 16 апреля 2026 · CORS: *

Quick Start

Для быстрого старта используйте демо-ключ:

hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
⚠ Внимание: Демо-ключ общий для всех пользователей. Лимит 5 запросов/мин считается по самому ключу, а не по IP — при активном использовании другими людьми ты попадёшь под общие ограничения. Для стабильной работы сгенерируй свой ключ ниже.

Пример запроса:

curl -X POST https://happy-decoder.cc/api/v1/decrypt \
  -H "Authorization: Bearer hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
  -H "Content-Type: application/json" \
  -d '{"url": "happ://crypt5/iaii..."}'

Свой API-ключ

Жмёшь — получаешь ключ. Никакой регистрации, никакой почты. Лимит ключа: 10 запросов/мин. Создание новых ключей — не чаще 1 раза в минуту с одного IP.

Endpoint

POST https://happy-decoder.cc/api/v1/decrypt
Headers:
Authorization: Bearer <ваш_ключ>
Content-Type: application/json
Body:
{"url": "happ://crypt5/..."}
Response (200):
{"decryptedUrl": "https://..."}
CORS:
Allow-Origin *, preflight OPTIONS → 204 без тела.

Рабочий пример

Скопируйте и вставьте — тот же payload используется в наших тестах, расшифровывается демо-ключом:

curl -sS -X POST https://happy-decoder.cc/api/v1/decrypt \
  -H "Authorization: Bearer hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
  -H "Content-Type: application/json" \
  -d '{"url":"happ://crypt5/iaiiXVJvtrK5hvgAzi561Ub7bhqQlpjpmsUNxtcqWsVviJdXWYA+I6O+tYTuBn/sFOrKzmcz=Kw=R4rgbtm2XcU7s6dCaKIe5RNS06AbbBwQGeItWP66cevhs9VRlJ3wi0JMgHGnTixCd1I3GzbzXS4lAl4UFPHLM3HD1RMBR0Ok7uko/Vt6cnacwhNH/Dp+e0h1Pz/cYKAHTF8tA7SsKbe+amxgad8KgOrHNRhqjYNE1xwaEy3i9oRRVl+TvtXtmaocLZgN+WyCt2sHHWSS1hGj6trwrqJIrOFwLrD0yODDZX1OuWDE3/mnfM9BgWjWjpuvcu9IVF9bFn66WFEw5Yi28DYGP+qyQ3EEC+u64QAbbnpJPb8DNZjcatdBc9swPN5PifFIxIxJ+VMDpRJgIs81phWN3x/7yqktB0rYulYuPG5ScA4yWEgErp1/68nwmjaG0XrH9H1zMYSAIf65eL3h5SWyx5fnNrUXQ0gbm02jd6hpAsW4lcGnJDENGA6xm/VQTn/JmZRxyU5lZCgNNd2DdiizI4jckj0bzxiAg9Hd70sRIBOQ1SDHyYLD5NWlRhiwMKmMSi9LXDa57euTDs1hN8zUVEf9sTy0AUy4W9aSnGIvxJhmYjXq87E/LZnsFsBbf6CuKPJ2pQTY4kHuCZNAdG/JZwyTxSR+BRoRnci6PJDENuSoAnNFEM6bQJZppbvgSr0tI1YNVMcx97uanNW2FcJFpXdEU1CZY5Pb09l54cNqQyF6=oxgadi"}'

Ожидаемый ответ:

{"decryptedUrl": "https://sometext"}

Формат happ://add/<url> — passthrough, просто срезает префикс:

curl -sS -X POST https://happy-decoder.cc/api/v1/decrypt \
  -H "Authorization: Bearer hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
  -H "Content-Type: application/json" \
  -d '{"url":"happ://add/https://sub.example.com/abc"}'

{"decryptedUrl": "https://sub.example.com/abc"}

Примеры кода

В примерах ниже — обработка ошибок. API всегда возвращает JSON; при ошибке — поле error. Status code в теле не дублируется, используйте HTTP-код.

# -sS: молча, но ошибки транспорта показывать
# -w: выводим HTTP-код после тела
curl -sS -w "\n%{http_code}\n" -X POST https://happy-decoder.cc/api/v1/decrypt \
  -H "Authorization: Bearer hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
  -H "Content-Type: application/json" \
  -d '{"url": "happ://crypt5/..."}'

# Пример парсинга через jq:
# curl -sS ... | jq -r '.decryptedUrl // .error'
import requests

def decrypt_happ(url: str, api_key: str) -> str:
    r = requests.post(
        "https://happy-decoder.cc/api/v1/decrypt",
        headers={"Authorization": f"Bearer {api_key}"},
        json={"url": url},
        timeout=10,
    )
    data = r.json()
    if "error" in data:
        raise RuntimeError(f"{r.status_code}: {data['error']}")
    return data["decryptedUrl"]

print(decrypt_happ("happ://crypt5/...", "hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"))
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

func decryptHapp(url, apiKey string) (string, error) {
    body, _ := json.Marshal(map[string]string{"url": url})
    req, _ := http.NewRequest("POST",
        "https://happy-decoder.cc/api/v1/decrypt",
        bytes.NewReader(body))
    req.Header.Set("Authorization", "Bearer "+apiKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    var out struct {
        DecryptedURL string `json:"decryptedUrl"`
        Error        string `json:"error"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
        return "", err
    }
    if out.Error != "" {
        return "", fmt.Errorf("%d: %s", resp.StatusCode, out.Error)
    }
    return out.DecryptedURL, nil
}

func main() {
    plain, err := decryptHapp("happ://crypt5/...", "hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6")
    if err != nil { fmt.Println("err:", err); return }
    fmt.Println(plain)
}
using System.Net.Http;
using System.Text;
using System.Text.Json;

async Task<string> DecryptHapp(string url, string apiKey) {
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");

    var body = new StringContent(
        JsonSerializer.Serialize(new { url }),
        Encoding.UTF8, "application/json");

    var resp = await client.PostAsync("https://happy-decoder.cc/api/v1/decrypt", body);
    var json = await resp.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(json);
    var root = doc.RootElement;

    if (root.TryGetProperty("error", out var err)) {
        throw new Exception($"{(int)resp.StatusCode}: {err.GetString()}");
    }
    return root.GetProperty("decryptedUrl").GetString()!;
}

Console.WriteLine(await DecryptHapp("happ://crypt5/...", "hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"));
// Работает из браузера (CORS включён) и из Node.js (18+)
async function decryptHapp(url, apiKey) {
  const r = await fetch("https://happy-decoder.cc/api/v1/decrypt", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ url })
  });
  const data = await r.json();
  if (data.error) throw new Error(`${r.status}: ${data.error}`);
  return data.decryptedUrl;
}

decryptHapp("happ://crypt5/...", "hd_demo_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6")
  .then(console.log)
  .catch(err => console.error(err));

Rate Limiting

Demo-ключ
5 запросов / мин
Персональный ключ
10 запросов / мин

Счётчик — per-key, не per-IP. Сколько бы IP вы ни использовали с одним ключом — лимит общий.

Алгоритм — sliding window: окно в 60 секунд скользит от момента каждого запроса. Счётчик уменьшается по мере истечения старых запросов, а не сбрасывается раз в минуту.

При превышении лимита сервер вернёт 429:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{"error": "rate limit exceeded"}

Коды ошибок

Все ответы — JSON с Content-Type: application/json, включая ошибки.

CodeResponseОписание
200 {"decryptedUrl": "..."} Успешная расшифровка
204 нет тела Ответ на CORS preflight (OPTIONS)
400 {"error": "invalid request body"} Некорректный JSON в теле запроса
400 {"error": "crypt5: RSA decrypt failed..."} Ошибка расшифровки (невалидные или чужие данные)
400 {"error": "crypt5: payload too short..."} Повреждённый или обрезанный payload
401 {"error": "missing or invalid api key"} Отсутствует или неверный API-ключ
403 {"error": "api key is inactive"} Ключ деактивирован администратором
405 {"error": "method not allowed, use POST"} Использован неверный HTTP-метод
429 {"error": "rate limit exceeded"} Превышен лимит запросов (см. Rate Limiting)

Поддерживаемые форматы

Universal Proxy Link

Эндпоинт /p/ — публичный HTTP-прокси для подписок. Идёт за вашей подпиской с заголовками выбранного клиента (Happ User-Agent, HWID, модель устройства) и возвращает тело как есть. Конструктор ссылки с UI — на странице Proxy.

В отличие от /api/v1/decrypt, этот эндпоинт не требует API-ключа: его задача — быть подставленным напрямую в любой xray/Mihomo/sing-box-клиент.

GET https://happy-decoder.cc/p/
Query parameters:
u — URL подписки (https://, http://, happ://crypt*/, happ://add/) — обязательный
ua — User-Agent (по умолчанию Happ/3.17.0; none — не отправлять)
hwid 1/0 или произвольное значение HWID
os android (по умолчанию) или ios
ver — версия ОС, по умолчанию 14
model — модель устройства, по умолчанию Pixel 8
seed 1: HWID детерминированно из URL подписки
Response (200): тело подписки as-is, плюс ключевые заголовки (Content-Type, Profile-Title, Subscription-Userinfo, Profile-Update-Interval) проброшены от апстрима.

Примеры

# Минимум: только URL подписки (дефолтный Happ-identity)
curl -sS 'https://happy-decoder.cc/p/?u=https%3A%2F%2Fexample.com%2Fsub'

# Своя модель и версия iOS
curl -sS 'https://happy-decoder.cc/p/?u=https%3A%2F%2Fexample.com%2Fsub&os=ios&ver=17.4&model=iPhone+16+Pro'

# Зашифрованная happ://crypt5/ ссылка
curl -sS 'https://happy-decoder.cc/p/?u=happ%3A%2F%2Fcrypt5%2Fiaii...'

# Детерминированный HWID, чтобы провайдер видел один и тот же device-id
curl -sS 'https://happy-decoder.cc/p/?u=https%3A%2F%2Fexample.com%2Fsub&seed=1'

Лимиты

Запросов / мин на IP
30
Таймаут запроса к апстриму
10 сек
Максимальный размер ответа
2 МБ

Коды ошибок

CodeТелоОписание
400missing 'u' parameterНе передан URL подписки
400invalid url schemeПоддерживаются только http(s):// и happ://
400decrypt: ...Не удалось расшифровать happ://crypt* ссылку
403private/local address blockedАпстрим резолвится в приватную/локальную сеть
429rate limit exceededПревышен лимит 30 запросов/мин с вашего IP
502upstream: ...Апстрим не отвечает / timeout / TLS-ошибка
502upstream returned HTTP NNNАпстрим вернул ≥ 400 (текст ответа проксируется как есть)

Приватность

В логах сохраняется только SHA-256-хеш URL, домен апстрима и факт обращения. Сама ссылка подписки в открытом виде не сохраняется.

Health check

Эндпоинт для проверки доступности сервиса. Возвращает 200 OK когда сервис работает. Используйте в uptime-мониторах.

curl -sS https://happy-decoder.cc/health
{"status": "ok"}