{"openapi":"3.0.3","info":{"title":"SuperStack public tools","version":"1.0.0","description":"Free public tools from SuperStack: South African tax estimates and dates, calendars, rand reference rates, and checks for mail, DNS, TLS and AWS waste. No account. No credentials. Anonymous usage counts only."},"servers":[{"url":"https://superstack.co.za"}],"paths":{"/api/tools":{"get":{"operationId":"getOpenApi","summary":"OpenAPI document for the public tools","responses":{"200":{"description":"This document."}}}},"/api/tools/email-health":{"post":{"operationId":"email-health","summary":"Email domain health check for SPF and DKIM","description":"Check SPF, DKIM, DMARC, MX, MTA-STS and BIMI for a public domain, including .co.za mail. A plain-language grade and the fix for each record. Nothing is stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["domain"],"properties":{"domain":{"type":"string","description":"A public domain, such as example.co.za."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/website-scan":{"post":{"operationId":"website-scan","summary":"Website safety and speed scan","description":"Check HTTPS, TLS, security headers, redirects and basic SEO tags for a public website. A PageSpeed score is included when a key is configured. The address is not stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","description":"A public http or https address."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/aws-bill":{"post":{"operationId":"aws-bill","summary":"AWS bill sanity check for common waste","description":"A checklist for idle servers, NAT gateways, old snapshots, unattached addresses and missing savings plans. No AWS login and no credentials. Useful for accounts billed in rand or dollars. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"monthlyBill":{"type":"string","description":"Optional monthly total, digits only. Used in the wording, not stored."},"currency":{"type":"string","description":"USD or ZAR.","enum":["USD","ZAR"]},"idleEc2":{"type":"string","description":"Whether some EC2 instances look idle.","enum":["yes","no","unsure"]},"natGateways":{"type":"string","description":"Whether the account uses NAT gateways.","enum":["yes","no","unsure"]},"oldSnapshots":{"type":"string","description":"Whether snapshots older than 90 days are kept.","enum":["yes","no","unsure"]},"unattachedEips":{"type":"string","description":"Whether Elastic IPs are unattached.","enum":["yes","no","unsure"]},"missingSavingsPlan":{"type":"string","description":"Whether steady compute has no Savings Plan.","enum":["yes","no","unsure"]},"idleLoadBalancers":{"type":"string","description":"Whether load balancers have little traffic.","enum":["yes","no","unsure"]},"endlessLogs":{"type":"string","description":"Whether log groups have no retention limit.","enum":["yes","no","unsure"]},"gp2Volumes":{"type":"string","description":"Whether gp2 volumes are still in use.","enum":["yes","no","unsure"]}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/dns-lookup":{"post":{"operationId":"dns-lookup","summary":"DNS lookup, nameservers and domain expiry","description":"Look up A, AAAA, MX, NS, TXT, CNAME and SOA records, plus nameservers, registrar and expiry from RDAP, and whether two public resolvers agree. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["domain"],"properties":{"domain":{"type":"string","description":"A public domain, such as example.co.za."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/ssl-check":{"post":{"operationId":"ssl-check","summary":"SSL certificate expiry and chain checker","description":"See the issuer, expiry, chain and days left on a public site’s TLS certificate. The check does not accept private keys. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["host"],"properties":{"host":{"type":"string","description":"A public hostname, such as example.co.za."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/http-headers":{"post":{"operationId":"http-headers","summary":"HTTP header and redirect chain checker","description":"Follow the redirect chain of a public URL and list the response headers. Cookie and credential values are hidden. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","description":"A public http or https address."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/kubernetes-lint":{"post":{"operationId":"kubernetes-lint","summary":"Kubernetes YAML linter for common mistakes","description":"Flag missing resource limits, latest image tags, privileged containers and missing probes. The manifest is checked and then discarded. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["yaml"],"properties":{"yaml":{"type":"string","description":"A Kubernetes manifest. It is checked and discarded."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/cron":{"post":{"operationId":"cron","summary":"Cron expression explainer in South African time","description":"Explain a five-field cron expression and list the next run times. Africa/Johannesburg (SAST) is the default timezone. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["expression"],"properties":{"expression":{"type":"string","description":"Five cron fields, or a name such as @daily."},"timezone":{"type":"string","description":"IANA timezone. Defaults to Africa/Johannesburg. SAST is accepted."},"count":{"type":"integer","description":"How many upcoming times to return, from 1 to 8. Defaults to 5."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/agent-ready":{"post":{"operationId":"agent-ready","summary":"Agent-readiness check for robots and llms.txt","description":"Check whether a public site publishes robots.txt, llms.txt, a sitemap, structured data and an OpenAPI document. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","description":"A public website address."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/paye":{"post":{"operationId":"paye","summary":"PAYE and UIF salary calculator for South Africa","description":"Estimate monthly PAYE and UIF from the SARS 2026/2027 individual tables, dated 25 February 2026. Medical scheme credits are optional. Not tax advice, and nothing is stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["pay"],"properties":{"pay":{"type":"number","description":"Gross pay in rand, before deductions."},"period":{"type":"string","description":"month or year. Defaults to month.","enum":["month","year"]},"ageBand":{"type":"string","description":"under-65, 65-74 or 75-plus. Defaults to under-65.","enum":["under-65","65-74","75-plus"]},"medicalMembers":{"type":"integer","description":"People on a registered medical scheme, including the taxpayer. 0 if none."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/vat":{"post":{"operationId":"vat","summary":"VAT calculator at the South African rate","description":"Add or extract VAT at the 15% standard rate from the 2026/27 SARS Budget tax guide. Zero-rated supplies are not included. The amount is not stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["amount"],"properties":{"amount":{"type":"number","description":"An amount in rand."},"mode":{"type":"string","description":"add (amount excludes VAT) or extract (amount includes VAT). Defaults to add.","enum":["add","extract"]}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/freelancer-rate":{"post":{"operationId":"freelancer-rate","summary":"Freelancer rate calculator in rand","description":"Turn a target income in rand into a daily and hourly rate, after leave, public holidays and a rough income-tax estimate. VAT can be shown on top. Nothing is stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["target"],"properties":{"target":{"type":"number","description":"Target annual amount in rand."},"targetMeans":{"type":"string","description":"before-tax or take-home. Defaults to before-tax.","enum":["before-tax","take-home"]},"year":{"type":"integer","description":"Calendar year used for weekdays and public holidays."},"leaveDays":{"type":"integer","description":"Weekdays of leave. Defaults to 15."},"sickDays":{"type":"integer","description":"Weekdays of sick leave held back. Defaults to 0."},"nonBillablePercent":{"type":"integer","description":"Share of working days that are not billed, 0 to 80. Defaults to 20."},"hoursPerDay":{"type":"integer","description":"Hours in a billed day, 1 to 12. Defaults to 8."},"annualExpenses":{"type":"number","description":"Business expenses to cover in the rate, in rand."},"ageBand":{"type":"string","description":"under-65, 65-74 or 75-plus, for the tax estimate.","enum":["under-65","65-74","75-plus"]},"vatRegistered":{"type":"boolean","description":"When true, the result also shows the rate including 15% VAT."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/provisional-tax":{"post":{"operationId":"provisional-tax","summary":"South African provisional tax dates","description":"First, second and voluntary third provisional tax dates for a month-end year of assessment. Weekend and public-holiday dates move to the previous business day. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"yearEnd":{"type":"string","description":"Financial year-end as YYYY-MM-DD, the last day of a month. Defaults to the current February year-end."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/sa-holidays":{"post":{"operationId":"sa-holidays","summary":"South African public holidays","description":"Public holidays for a calendar year under the Public Holidays Act, including the Monday after a Sunday holiday. Gazette days declared later are not included. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"year":{"type":"integer","description":"Calendar year from 1994 to 2100. Defaults to the current year."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/business-days":{"post":{"operationId":"business-days","summary":"South African business-day counter","description":"Count Monday-to-Friday days between two dates, excluding South African public holidays. Both dates are included when they are business days. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["from","to"],"properties":{"from":{"type":"string","description":"Start date, YYYY-MM-DD, included when it is a business day."},"to":{"type":"string","description":"End date, YYYY-MM-DD, included when it is a business day."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/zar-rates":{"post":{"operationId":"zar-rates","summary":"Rand exchange rates from the ECB reference","description":"Convert rand using European Central Bank reference rates published through Frankfurter. The figure is cached for a few hours, dated, and is not a bank’s rate. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"amount":{"type":"number","description":"Rand amount. Defaults to 1."},"quote":{"type":"string","description":"USD, EUR, GBP, AUD, CAD, CHF, JPY, CNY, INR, BWP, or ALL. Defaults to ALL.","enum":["ALL","USD","EUR","GBP","AUD","CAD","CHF","JPY","CNY","INR","BWP"]}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/eks-cost":{"post":{"operationId":"eks-cost","summary":"AWS EKS and EC2 on-demand cost estimate","description":"Estimate an EKS control plane plus EC2 workers from published on-demand prices dated 12 October 2026. Cape Town and Virginia. Disks, NAT and data transfer are excluded. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"region":{"type":"string","description":"af-south-1 or us-east-1. Defaults to Cape Town.","enum":["af-south-1","us-east-1"]},"instance":{"type":"string","description":"An instance type in the dated price table, such as t3.medium."},"nodes":{"type":"integer","description":"Worker nodes. Defaults to 3."},"clusters":{"type":"integer","description":"EKS clusters. Defaults to 1."},"support":{"type":"string","description":"standard (USD 0.10 per cluster-hour) or extended (USD 0.60).","enum":["standard","extended"]},"hours":{"type":"integer","description":"Hours in the month. Defaults to 730."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/terraform-plan":{"post":{"operationId":"terraform-plan","summary":"Terraform plan explainer","description":"Summarise a Terraform plan as creates, updates, replacements and destroys. The page explains the plan in the browser. Attribute values are not returned and the plan is not stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"plan":{"type":"string","description":"Plan JSON or the text summary. Omit it to record a browser use without sending the plan."},"format":{"type":"string","description":"auto, json or text. Defaults to auto.","enum":["auto","json","text"]},"event":{"type":"string","description":"Set to browser when the page counted a local use and did not send the plan."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/dockerfile":{"post":{"operationId":"dockerfile","summary":"Dockerfile checker","description":"Flag latest tags, a missing USER, piped installers and secret-looking ENV names. The page checks the file in the browser. The file is not stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"dockerfile":{"type":"string","description":"Dockerfile text. Omit it to record a browser use without sending the file."},"event":{"type":"string","description":"Set to browser when the page counted a local use and did not send the file."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/cidr":{"post":{"operationId":"cidr","summary":"IPv4 CIDR and subnet calculator","description":"Network, broadcast, netmask and usable hosts for an IPv4 CIDR, with an optional split into smaller subnets. Nothing is stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["cidr"],"properties":{"cidr":{"type":"string","description":"An IPv4 CIDR, such as 10.0.0.0/24."},"split":{"type":"integer","description":"Optional longer prefix to split into, at most 256 subnets."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/json-yaml":{"post":{"operationId":"json-yaml","summary":"JSON and YAML formatter","description":"Check and pretty-print JSON or YAML. The page formats the document in the browser. The document is not stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"text":{"type":"string","description":"JSON or YAML text. Omit it to record a browser use without sending the document."},"format":{"type":"string","description":"auto, json or yaml. Defaults to auto.","enum":["auto","json","yaml"]},"event":{"type":"string","description":"Set to browser when the page counted a local use and did not send the document."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/jwt":{"post":{"operationId":"jwt","summary":"JWT decoder that stays in the browser","description":"Read the header and payload of a JSON Web Token in the browser. The token is never sent to the server, the signature is not checked, and nothing is stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/password":{"post":{"operationId":"password","summary":"Password safety guide and generator","description":"Read a short guide for passwords used on South African mail and banking, then generate one in the browser with crypto.getRandomValues. The password is not sent to the server and is not stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":[],"properties":{"length":{"type":"integer","description":"Length from 12 to 64. Defaults to 20."},"lower":{"type":"boolean","description":"Include lowercase letters. Defaults to true."},"upper":{"type":"boolean","description":"Include uppercase letters. Defaults to true."},"digits":{"type":"boolean","description":"Include digits. Defaults to true."},"symbols":{"type":"boolean","description":"Include symbols. Defaults to false."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/qr":{"post":{"operationId":"qr","summary":"QR code maker for a link or a short note","description":"Turn a link or up to 500 characters of text into a QR code. The text is used to draw the image and then discarded. It is not stored. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["text"],"properties":{"text":{"type":"string","description":"The text to encode, up to 500 characters."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}},"/api/tools/email-scam":{"post":{"operationId":"email-scam","summary":"Is this email a scam? A cautious check","description":"Paste a suspicious email. The check looks for urgency, payment demands and South African brand impersonation, then reads public DNS for the sender domain. The message is not stored, and a quiet result is not proof it is genuine. Nothing submitted is stored, apart from an anonymous count.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["text"],"properties":{"text":{"type":"string","description":"The email, including the From line. It is checked and discarded."},"sender":{"type":"string","description":"Optional sender address, if it is not in the pasted text."}}}}}},"responses":{"200":{"description":"The check result. No credentials are returned."},"400":{"description":"The input was not valid."},"429":{"description":"Too many checks from this network. Wait and try again."}}}}}}