Libraries, code examples and AI instruction
These examples calculate SHA-256 hashes locally, then send only hash strings to HashCheck. File contents are not uploaded. You can also copy the AI Agent Instruction into your coding assistant.
AI Agent Instruction
Copy this instruction into your AI coding agent when you want it to integrate HashCheck into your project.
You are integrating Xexle HashCheck into this project.
Goal:
- Calculate file hashes locally.
- Send only hash strings to HashCheck.
- Never upload file contents to HashCheck.
- Add clear functions that fit the host project style and language.
Hash algorithm:
- Prefer SHA-256 for new code.
- SHA-256 output is 64 lowercase hexadecimal characters.
- Hash the exact file bytes in streaming/chunked mode so large files do not load fully into memory.
- Normalize generated hashes to lowercase.
- Validate input hashes before sending them. HashCheck accepts lowercase/uppercase hex strings with length 32, 40, 64 or 128, but generated hashes should use SHA-256 unless the user explicitly requests another algorithm.
Required functions:
- hashFile(filePath): return the SHA-256 hash of one file.
- hashDirectory(directoryPath): recursively calculate hashes for files in a directory. Return a map/list that preserves file path to hash.
- checkHashes(hashes): send hashes to the HashCheck lookup API and return parsed JSON.
- checkFile(filePath): convenience wrapper around hashFile + checkHashes.
- checkDirectory(directoryPath): convenience wrapper around hashDirectory + batched checkHashes.
HashCheck API:
- Lookup endpoint: https://hashcheck.xexle.com/api/check
- Add endpoint: https://hashcheck.xexle.com/api/add
- Renew validator endpoint: https://hashcheck.xexle.com/api/renew-validator
- Send requests as GET or POST form fields.
- Parameter: hashes=hash1,hash2,hash3
- Default lookup returns only found hashes.
- Use include_missing=1 when the project needs explicit 0 values for hashes not found.
- Response shape: { "status": true, "data": { "hash": code }, "message": "optional" }
Lookup result codes:
- 0: hash not found in HashCheck database. Returned only when include_missing=1 or found_only=0 is used.
- 1: hash is present in Xexle service list.
- 2: hash was submitted by a trusted validator.
- 3: hash was submitted by a user and is not verified.
Limits and batching:
- Lookup limit: up to 1000 valid hashes per request.
- Batch directory checks into chunks of at most 1000 hashes.
- Prefer batching over sending one request per file.
- If a request fails or returns a message about cooldown/rate limiting, surface that message to the caller and do not spin in a tight retry loop.
- Use reasonable HTTP timeouts.
Adding hashes:
- Anonymous online/user additions are limited to 3 valid hashes per request and one request every 15 minutes per IP.
- Programmatic/API additions should use a trusted validator token.
- Validator add endpoint: https://hashcheck.xexle.com/api/add?hashes=hash1,hash2&validator=VALIDATOR_TOKEN
- Validator renew endpoint: https://hashcheck.xexle.com/api/renew-validator?validator=OLD_VALIDATOR_TOKEN
- Renewing a validator token removes the old validator token and returns a new one for the same validator record.
- Keep validator tokens only on trusted server-side systems.
- Never put validator tokens into browser JavaScript, mobile app bundles, public repositories, logs, screenshots or client-side config.
- If there is no validator token, do not build bulk add automation. Direct the user to the online Add page instead.
Security and compliance behavior:
- Do not claim that a hash match proves a file is illegal or criminal.
- Treat results as technical signals.
- Code 3 is user-submitted and unverified; do not treat it as a verified blocklist.
- Do not send file names, file contents or unnecessary personal data to HashCheck unless the user explicitly asks and there is a lawful reason.
- If the host project logs API calls, avoid logging validator tokens and avoid logging sensitive file paths by default.
Implementation quality:
- Follow the existing project coding style.
- Use standard libraries for hashing and HTTP where practical.
- Stream file reads.
- Deduplicate hashes before sending.
- Preserve mapping from file path to hash and from hash to result code.
- Return structured errors instead of throwing raw HTTP details into UI.
- Add small tests or examples for hashFile, hashDirectory and checkHashes when the project has a test framework.
- Document the returned codes near the integration point.
Python
import hashlib
import json
import os
import urllib.parse
import urllib.request
API_URL = "https://hashcheck.xexle.com/api/check"
def hash_file(path):
digest = hashlib.sha256()
with open(path, "rb") as file:
for chunk in iter(lambda: file.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def hash_directory(path):
hashes = {}
for root, _, files in os.walk(path):
for name in sorted(files):
file_path = os.path.join(root, name)
if os.path.isfile(file_path):
hashes[file_path] = hash_file(file_path)
return hashes
def check_hashes(hashes):
values = ",".join(hashes)
url = API_URL + "?" + urllib.parse.urlencode({"hashes": values})
with urllib.request.urlopen(url, timeout=30) as response:
return json.loads(response.read().decode("utf-8"))
if __name__ == "__main__":
file_hash = hash_file("example.bin")
print(file_hash)
print(check_hashes([file_hash]))
Node.js
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { readdir } from "node:fs/promises";
import path from "node:path";
const API_URL = "https://hashcheck.xexle.com/api/check";
export function hashFile(filePath) {
return new Promise((resolve, reject) => {
const hash = createHash("sha256");
const stream = createReadStream(filePath);
stream.on("data", chunk => hash.update(chunk));
stream.on("error", reject);
stream.on("end", () => resolve(hash.digest("hex")));
});
}
export async function hashDirectory(dirPath) {
const result = {};
const entries = await readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
Object.assign(result, await hashDirectory(fullPath));
} else if (entry.isFile()) {
result[fullPath] = await hashFile(fullPath);
}
}
return result;
}
export async function checkHashes(hashes) {
const url = new URL(API_URL);
url.searchParams.set("hashes", hashes.join(","));
const response = await fetch(url, { headers: { Accept: "application/json" } });
return await response.json();
}
const fileHash = await hashFile("example.bin");
console.log(fileHash);
console.log(await checkHashes([fileHash]));
PHP
<?php
const HASHCHECK_API_URL = 'https://hashcheck.xexle.com/api/check';
function hashFileSha256(string $path): string
{
return hash_file('sha256', $path);
}
function hashDirectorySha256(string $path): array
{
$out = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$out[$file->getPathname()] = hashFileSha256($file->getPathname());
}
}
return $out;
}
function checkHashes(array $hashes): array
{
$query = http_build_query(['hashes' => implode(',', $hashes)]);
$json = file_get_contents(HASHCHECK_API_URL . '?' . $query);
return json_decode($json ?: '{}', true) ?: [];
}
$fileHash = hashFileSha256('example.bin');
print_r(checkHashes([$fileHash]));
Go
package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
)
const apiURL = "https://hashcheck.xexle.com/api/check"
func HashFile(path string) (string, error) {
file, err := os.Open(path)
if err != nil {
return "", err
}
defer file.Close()
sum := sha256.New()
if _, err := io.Copy(sum, file); err != nil {
return "", err
}
return hex.EncodeToString(sum.Sum(nil)), nil
}
func HashDirectory(root string) (map[string]string, error) {
out := map[string]string{}
err := filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
if err != nil || entry.IsDir() {
return err
}
hash, err := HashFile(path)
if err != nil {
return err
}
out[path] = hash
return nil
})
return out, err
}
func CheckHashes(hashes []string) (map[string]any, error) {
client := &http.Client{Timeout: 30 * time.Second}
values := url.Values{}
values.Set("hashes", strings.Join(hashes, ","))
resp, err := client.Get(apiURL + "?" + values.Encode())
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result map[string]any
err = json.NewDecoder(resp.Body).Decode(&result)
return result, err
}
func main() {
hash, err := HashFile("example.bin")
if err != nil {
panic(err)
}
result, err := CheckHashes([]string{hash})
if err != nil {
panic(err)
}
fmt.Println(result)
}
Bash
#!/usr/bin/env bash
set -euo pipefail
API_URL="https://hashcheck.xexle.com/api/check"
hash_file() {
shasum -a 256 "$1" | awk '{print $1}'
}
hash_directory() {
find "$1" -type f -print0 | sort -z | while IFS= read -r -d '' file; do
printf '%s %s\n' "$(hash_file "$file")" "$file"
done
}
check_hashes() {
local hashes="$1"
curl -sS -G "$API_URL" --data-urlencode "hashes=$hashes"
}
file_hash="$(hash_file "example.bin")"
check_hashes "$file_hash"
Integration Notes
- Use batching for large directories. The public lookup limit is 1000 hashes per request.
- Keep validator tokens only on trusted server-side systems.
- Use
include_missing=1only when you need explicit0values for unknown hashes. - For high-volume trusted ingestion, use validator submissions or the internal bulk import process rather than browser calls.