humantone

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 4, 2026 License: MIT Imports: 17 Imported by: 0

README

humantone-go

Official Go client for the HumanTone API. Rewrites AI-generated text into natural-sounding prose. Adds an AI Likelihood Indicator that scores how AI-like any text reads.

Go Reference Go Report Card

Install

go get github.com/humantone/humantone-go@latest

Requires Go 1.22 or later. Stdlib only, zero external dependencies.

Quickstart

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/humantone/humantone-go"
)

func main() {
    client, err := humantone.NewClient(humantone.Config{
        APIKey: os.Getenv("HUMANTONE_API_KEY"),
    })
    if err != nil {
        log.Fatal(err)
    }

    result, err := client.Humanize(context.Background(), humantone.HumanizeRequest{
        Text: "Your AI-generated draft goes here. At least 30 words.",
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(result.Text)
    fmt.Printf("Credits used: %d\n", result.CreditsUsed)
}

The client also picks up HUMANTONE_API_KEY from the environment when Config.APIKey is empty:

client, _ := humantone.NewClient(humantone.Config{})

API

client.Humanize(ctx, req)
Field Type Default Notes
Text string required Min 30 words. Max depends on plan (Basic 750, Standard 1000, Pro 1500).
Level HumanizationLevel LevelStandard LevelAdvanced and LevelExtreme are English-only.
OutputFormat OutputFormat FormatText SDK default is FormatText even though the API default is html.
CustomInstructions string "" Free-form rewrite guidance. Max 1000 chars.

Returns *HumanizeResult with Text, OutputFormat, CreditsUsed, RequestID.

client.Detect(ctx, text)

Returns AI likelihood score 0-100. Free, but limited to 30 calls per day per account.

result, err := client.Detect(ctx, "Some text...")
fmt.Println(result.AIScore)
client.Account.Get(ctx)
info, err := client.Account.Get(ctx)
fmt.Println(info.Plan.Name)
fmt.Println(info.Credits.Total)
fmt.Println(info.Plan.MaxWords)
if info.Subscription.ExpiresAt != nil {
    fmt.Println(info.Subscription.ExpiresAt.Format(time.RFC3339))
}

Error handling

All SDK errors are *humantone.Error wrapping a sentinel.

import "errors"

result, err := client.Humanize(ctx, req)
if err != nil {
    switch {
    case errors.Is(err, humantone.ErrInsufficientCredits):
        fmt.Println("Out of credits")
    case errors.Is(err, humantone.ErrRateLimit):
        var apiErr *humantone.Error
        if errors.As(err, &apiErr) {
            fmt.Printf("Wait %ds\n", apiErr.RetryAfterSeconds)
        }
    case errors.Is(err, humantone.ErrAuthentication):
        fmt.Println("Bad API key")
    default:
        var apiErr *humantone.Error
        if errors.As(err, &apiErr) {
            fmt.Printf("HumanTone error (%s): %s\n", apiErr.Code, apiErr.Message)
        } else {
            fmt.Printf("Unknown error: %v\n", err)
        }
    }
}

Sentinels:

  • ErrAuthentication, ErrPermission, ErrRateLimit
  • ErrInsufficientCredits, ErrDailyLimitExceeded
  • ErrInvalidRequest, ErrNotFound
  • ErrAPIError, ErrTimeout, ErrNetwork
  • ErrInvalidAPIKey (for both missing and malformed)

*Error fields: Code, StatusCode, Message, RequestID, Details, Retryable, RetryAfterSeconds, TimeToNextRenew.

Configuration

client, _ := humantone.NewClient(humantone.Config{
    APIKey:      "ht_...",                    // or HUMANTONE_API_KEY env
    BaseURL:     "https://api.humantone.io",  // or HUMANTONE_BASE_URL env, default
    Timeout:     120 * time.Second,
    MaxRetries:  2,
    RetryOnPost: false,                        // POST endpoints retry only when explicit
    UserAgent:   "my-app/1.0",                 // appended to default UA after a single space
})

Retry behavior

The SDK retries Account.Get on network errors, 5xx, and 429 (up to 2 retries). POST methods (Humanize, Detect) do not retry on network or 5xx by default. Humanize debits credits, so a retried request risks double-billing. Set Config.RetryOnPost: true to opt in. 429 always retries on every method.

Retry-After headers are honored in both numeric (seconds) and HTTP-date formats.

Cancellation

All methods accept context.Context. Use context.WithTimeout or context.WithCancel to abort in-flight requests:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
result, err := client.Humanize(ctx, req)

Limits to remember

  • Per-request word limit. Basic 750, Standard 1000, Pro 1500. Inputs must be at least 30 words.
  • Credits. Humanize consumes 1 credit per 100 words. Account checks and AI likelihood checks do not consume credits.
  • AI likelihood quota. 30 checks per day per account, shared between the HumanTone web app and any API or SDK usage. Resets at midnight UTC.
  • API access. Included on all paid plans. Free trial accounts cannot use the API.

Get an API key

Sign up at humantone.io. The HumanTone API is paid only.

License

MIT

Documentation

Overview

Package humantone is the official Go client for the HumanTone API (https://humantone.io). It rewrites AI-generated text into natural-sounding human prose and provides an AI Likelihood Indicator that scores how AI-like any text reads.

The package exposes three endpoints via *Client: Humanize, Detect, and Account.Get. All methods accept a context.Context for cancellation and deadlines, and return either a typed result or an error wrapping a sentinel (such as ErrAuthentication or ErrInsufficientCredits) inside a *Error struct that carries the HTTP status, request ID, and other context.

Quickstart

client, err := humantone.NewClient(humantone.Config{
    APIKey: os.Getenv("HUMANTONE_API_KEY"),
})
if err != nil {
    log.Fatal(err)
}

result, err := client.Humanize(context.Background(), humantone.HumanizeRequest{
    Text: "Your AI-generated draft goes here. At least 30 words.",
})
if err != nil {
    log.Fatal(err)
}

fmt.Println(result.Text)

The package has no third-party dependencies — only the Go standard library. See https://humantone.io/docs/api/ for the underlying REST API documentation.

Index

Constants

View Source
const SDKVersion = "0.0.1"

SDKVersion is the hardcoded version of this SDK. It is bumped manually before each git tag and serves as a fallback when runtime build-info lookup does not surface a usable module version (for example when the SDK is compiled as the main module during development).

Variables

View Source
var (
	ErrAuthentication      = errors.New("humantone: authentication failed")
	ErrPermission          = errors.New("humantone: permission denied")
	ErrRateLimit           = errors.New("humantone: rate limited")
	ErrInsufficientCredits = errors.New("humantone: insufficient credits")
	ErrDailyLimitExceeded  = errors.New("humantone: daily detection limit reached")
	ErrInvalidRequest      = errors.New("humantone: invalid request")
	ErrNotFound            = errors.New("humantone: not found")
	ErrAPIError            = errors.New("humantone: API error")
	ErrTimeout             = errors.New("humantone: request timed out")
	ErrNetwork             = errors.New("humantone: network error")
	ErrInvalidAPIKey       = errors.New("humantone: invalid API key format")
)

Sentinel errors returned by SDK methods. Use errors.Is to test for them.

Each *Error returned by the SDK wraps exactly one of these sentinels via Unwrap, so errors.Is(err, humantone.ErrAuthentication) (and so on) is the idiomatic way to discriminate failures by category. To inspect richer context — request ID, HTTP status, retry-after seconds, daily renewal window — use errors.As to obtain the *Error value.

Functions

This section is empty.

Types

type AccountInfo

type AccountInfo struct {
	Plan         Plan         `json:"plan"`
	Credits      Credits      `json:"credits"`
	Subscription Subscription `json:"subscription"`
	RequestID    string       `json:"-"`
}

AccountInfo is the response from Client.Account.Get.

type AccountResource

type AccountResource struct {
	// contains filtered or unexported fields
}

AccountResource exposes the /v1/account operations. Obtain it from Client.Account; do not construct directly.

func (*AccountResource) Get

Get fetches the account info: plan, credits, subscription state. It does not consume credits.

type Client

type Client struct {
	// Account exposes /v1/account operations. Always non-nil after NewClient.
	Account *AccountResource
	// contains filtered or unexported fields
}

Client is the entry point for all HumanTone SDK calls. It is safe for concurrent use by multiple goroutines once constructed.

Construct one with NewClient and reuse it across requests; the underlying *http.Client maintains its own connection pool.

func NewClient

func NewClient(cfg Config) (*Client, error)

NewClient validates the Config per §5 and constructs a *Client. It returns a *Error wrapping ErrInvalidAPIKey when the API key is missing (Code ErrCodeMissingAPIKey) or malformed (Code ErrCodeInvalidAPIKey). Validation is eager — the returned client never makes an HTTP request without first having seen a syntactically valid key.

func (*Client) Detect

func (c *Client) Detect(ctx context.Context, text string) (*DetectResult, error)

Detect returns the AI Likelihood Indicator score for the given text. The score ranges from 0 (clearly human) to 100 (clearly AI-generated).

/v1/detect is free of credit cost but capped at 30 calls per day per account, shared across the HumanTone web app and the API. Exceeding the cap returns *Error wrapping ErrDailyLimitExceeded with TimeToNextRenew indicating the seconds until the daily counter resets.

func (*Client) Humanize

func (c *Client) Humanize(ctx context.Context, req HumanizeRequest) (*HumanizeResult, error)

Humanize rewrites AI-generated text into natural-sounding human prose. Returns a *HumanizeResult on success; on failure returns a *Error wrapping one of the package's sentinel errors.

type Config

type Config struct {
	APIKey      string
	BaseURL     string
	Timeout     time.Duration
	MaxRetries  int
	RetryOnPost bool
	HTTPClient  *http.Client
	UserAgent   string
}

Config configures a Client. The zero value is not directly usable — NewClient performs validation per §5 of the spec — but every field has a safe default applied when left at its zero value:

  • APIKey: required (or set HUMANTONE_API_KEY); whitespace is trimmed.
  • BaseURL: defaults to "https://api.humantone.io"; HUMANTONE_BASE_URL is consulted if Config.BaseURL is empty/whitespace.
  • Timeout: defaults to 120 seconds.
  • MaxRetries: defaults to 2 (i.e., up to 3 total attempts).
  • RetryOnPost: defaults to false. When true, POST endpoints retry on transient transport, 5xx, and the reserved humanize 200+success:false case. 429 retries on POST regardless of this flag.
  • HTTPClient: optional; injected as-is. The SDK does not modify its Timeout field. When nil, the SDK constructs an *http.Client with Timeout = Config.Timeout.
  • UserAgent: optional suffix appended to the SDK's User-Agent string, separated by a single space. Whitespace is trimmed.

type Credits

type Credits struct {
	Trial        int `json:"trial"`
	Subscription int `json:"subscription"`
	Extra        int `json:"extra"`
	Total        int `json:"total"`
}

Credits is the per-bucket credit breakdown returned by /v1/account.

type DetectResult

type DetectResult struct {
	AIScore   int    `json:"ai_score"`
	RequestID string `json:"-"`
}

DetectResult is the output of Client.Detect. AIScore is the 0-100 AI likelihood; higher means more AI-like. RequestID, when non-empty, echoes the X-Request-Id of the response (or the body's request_id field, if the API ever starts returning it).

type Error

type Error struct {
	// Code is a stable machine-readable identifier for the error kind.
	Code ErrorCode

	// StatusCode is the HTTP status returned by the API; 0 for non-HTTP
	// failures such as local validation, network errors, or timeouts.
	StatusCode int

	// Message is the human-readable error description. For API-originated
	// errors it is the verbatim server message; for SDK-originated errors
	// it is the SDK's own description.
	Message string

	// RequestID echoes the API's request_id (from the response body or the
	// X-Request-Id header). Empty string when not provided.
	RequestID string

	// Details holds extra fields supplied by the v2 error shape, plus
	// diagnostic fields for parse and coercion failures (raw_body,
	// parse_error, coercion_error). Nil when there is nothing to report.
	Details map[string]any

	// Retryable indicates whether the underlying condition is, in
	// principle, transient. The SDK retry loop consults this together with
	// the HTTP method and Config.RetryOnPost to decide actual retries.
	Retryable bool

	// RetryAfterSeconds is populated for ErrRateLimit from the Retry-After
	// response header. 0 if the header was absent or unparseable.
	RetryAfterSeconds int

	// TimeToNextRenew is populated for ErrDailyLimitExceeded from the
	// API's time_to_next_renew body field. 0 if the field was omitted by
	// the server.
	TimeToNextRenew int
	// contains filtered or unexported fields
}

Error is the rich error type returned by all SDK methods. It always wraps exactly one sentinel (accessible via Unwrap and therefore via errors.Is), and carries diagnostic context useful for logging, retry decisions, and user-facing messages.

Always returned by the SDK as a pointer; obtain it from a returned error with errors.As:

var apiErr *humantone.Error
if errors.As(err, &apiErr) {
    log.Printf("request_id=%s code=%s", apiErr.RequestID, apiErr.Code)
}

func (*Error) Error

func (e *Error) Error() string

Error renders the error as a string suitable for logging.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the sentinel error this *Error wraps, enabling errors.Is to walk the chain.

type ErrorCode

type ErrorCode string

ErrorCode is a stable string identifier for the kind of error returned by the API or detected locally. Constants below cover every value the SDK can produce; additional values may appear in the future via the API's planned v2 error shape (body.error.code) and will be surfaced verbatim.

const (
	ErrCodeAuthentication      ErrorCode = "authentication_error"
	ErrCodePermission          ErrorCode = "permission_denied"
	ErrCodeRateLimit           ErrorCode = "rate_limit"
	ErrCodeInsufficientCredits ErrorCode = "insufficient_credits"
	ErrCodeDailyLimitExceeded  ErrorCode = "daily_limit_exceeded"
	ErrCodeInvalidRequest      ErrorCode = "invalid_request"
	ErrCodeNotFound            ErrorCode = "not_found"
	ErrCodeAPIError            ErrorCode = "api_error"
	ErrCodeTimeout             ErrorCode = "timeout"
	ErrCodeNetwork             ErrorCode = "network_error"
	ErrCodeInvalidAPIKey       ErrorCode = "invalid_api_key_format"
	ErrCodeMissingAPIKey       ErrorCode = "missing_api_key"
)

Top-level error codes corresponding to the sentinel errors and to the API's canonical error categories.

const (
	ErrCodeTextTooShort         ErrorCode = "text_too_short"
	ErrCodeTextTooLong          ErrorCode = "text_too_long"
	ErrCodeSafetyCheckFailed    ErrorCode = "safety_check_failed"
	ErrCodeLanguageNotSupported ErrorCode = "language_not_supported"
)

Refined error codes that the SDK surfaces for specific recognized message patterns on /v1/humanize. These all wrap ErrInvalidRequest as the sentinel (or ErrInsufficientCredits for credits) but discriminate the failure mode for callers that want finer-grained handling.

type HumanizationLevel

type HumanizationLevel string

HumanizationLevel selects the rewriting intensity for /v1/humanize.

const (
	LevelStandard HumanizationLevel = "standard"
	LevelAdvanced HumanizationLevel = "advanced"
	LevelExtreme  HumanizationLevel = "extreme"
)

Recognized humanization levels. LevelAdvanced and LevelExtreme are English-only at the API; sending them with non-English text returns an ErrInvalidRequest with code ErrCodeLanguageNotSupported.

type HumanizeRequest

type HumanizeRequest struct {
	Text               string
	Level              HumanizationLevel
	OutputFormat       OutputFormat
	CustomInstructions string
}

HumanizeRequest is the input to Client.Humanize.

Text is required (minimum 30 words, plan-dependent maximum). Level and OutputFormat have safe defaults applied when left at the zero value. CustomInstructions, when set, must be at most 1000 characters; the SDK does not enforce this locally — the API will reject longer values.

type HumanizeResult

type HumanizeResult struct {
	Text         string       `json:"content"`
	OutputFormat OutputFormat `json:"output_format"`
	CreditsUsed  int          `json:"credits_used"`
	RequestID    string       `json:"request_id"`
}

HumanizeResult is the output of Client.Humanize.

type OutputFormat

type OutputFormat string

OutputFormat selects the response format for humanized content.

const (
	FormatText     OutputFormat = "text"
	FormatHTML     OutputFormat = "html"
	FormatMarkdown OutputFormat = "markdown"
)

Recognized output formats. The SDK defaults to FormatText (overriding the API's own default of FormatHTML) so that callers receive plain text unless they explicitly opt into HTML or markdown.

type Plan

type Plan struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	MaxWords       int    `json:"max_words"`
	MonthlyCredits int    `json:"monthly_credits"`
	APIAccess      bool   `json:"api_access"`
}

Plan describes the account's current subscription tier as returned by /v1/account.

type Subscription

type Subscription struct {
	Active    bool       `json:"active"`
	ExpiresAt *time.Time `json:"expires_at"`
}

Subscription describes the active subscription state. ExpiresAt is nil when the API omits the field or returns JSON null.

Directories

Path Synopsis
examples
account command
account is a small example program that fetches the account info: plan, remaining credits, and subscription expiry.
account is a small example program that fetches the account info: plan, remaining credits, and subscription expiry.
detect command
detect is a small example program that scores a sample text on the AI Likelihood Indicator.
detect is a small example program that scores a sample text on the AI Likelihood Indicator.
humanize command
humanize is a small example program that takes the text in HUMANTONE_API_KEY account credits and rewrites a fixed sample using the standard humanization level.
humanize is a small example program that takes the text in HUMANTONE_API_KEY account credits and rewrites a fixed sample using the standard humanization level.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL