A Cloudflare Worker-based API for programmatically accessing Google Forms. Get form structure and submit responses via simple REST API calls.
- 🚀 Fast: Runs on Cloudflare's edge network
- Simple REST API: GET form structure, POST to submit
- 📦 Serverless: No server management required
- 🌍 Global: Low latency worldwide via Cloudflare edge
- 🎨 Web UI: Beautiful landing page with API documentation & Online Test tool
To prevent abuse, the public instance includes a default rate limit (10 seconds cooldown per IP for POST requests). 為了防止濫用,預設包含頻率限制(POST 請求每 IP 需間隔 10 秒)。
For Developers:
If you clone this project, you can remove this limit in src/index.ts.
如果您 Clone 此專案自行部署,可以在 src/index.ts 中移除此限制。
Cloudflare Workers Free Tier has a limit of 100,000 requests per day (approx.). If you expect high traffic, please clone this repository and deploy it to your own Cloudflare account. Cloudflare Workers 免費方案每日約有 10 萬次請求限制。若您有大量使用需求,請務必 Clone 本專案並部署至您自己的 Cloudflare 帳號。
- Your Google Form must have email collection set to either "Do not collect" or "Responder input"
- Form must not require file uploads (forces Google sign-in)
- Form must be publicly accessible
# Install dependencies
npm install
# Development
npm start
# Deploy to Cloudflare Workers
npm run deploy- Open your Google Form
- Click "Send" → Get link
- Extract the ID from the URL between
/e/and/viewform
Example URL:
https://docs.google.com/forms/d/e/1FAIpQLSezfDEk03hYi9duf1vVSDGGBFAZq2zfPNw9_smS_8X2xmfzWQ/viewform
Form ID:
1FAIpQLSezfDEk03hYi9duf1vVSDGGBFAZq2zfPNw9_smS_8X2xmfzWQ
https://form.wiz.tw/g/<form_id>
Local development:
http://localhost:8787/g/<form_id>
Web UI: Visit the root URL (/) for interactive documentation:
https://form.wiz.tw/
Returns form metadata, sections, and all questions with their IDs, types, and options.
Request:
curl http://localhost:8787/g/1FAIpQLSezfDEk03hYi9duf1vVSDGGBFAZq2zfPNw9_smS_8X2xmfzWQResponse (Simple Form):
{
"title": "未命名表單",
"description": null,
"collectEmails": "NONE",
"sections": [
{
"id": null,
"title": null,
"questions": [
{
"title": "公司的MAIL",
"type": "TEXT",
"options": [],
"required": true,
"id": "1536632002"
}
]
}
],
"questions": [
{
"title": "公司的MAIL",
"type": "TEXT",
"options": [],
"required": true,
"id": "1536632002"
}
],
"error": false
}當表單使用「根據答案前往相關區段」功能時,API 會自動解析 Section 結構:
Response (Form with Sections):
{
"title": "訂餐表單",
"sections": [
{
"id": null,
"title": null,
"questions": [
{
"title": "樓層",
"type": "MULTIPLE_CHOICE",
"options": [
{ "value": "18樓", "goToSection": "1818063484" },
{ "value": "19樓1區", "goToSection": "1094976054" }
],
"required": true,
"id": "639819479"
}
]
},
{
"id": "1818063484",
"title": "餐點選擇:明德素食 / 泰私廚",
"questions": [
{
"title": "餐點",
"type": "MULTIPLE_CHOICE",
"options": [
"1.明德素食-胚芽+8樣配菜(全素)",
"2.泰私廚-打拋豬拌麵"
],
"required": true,
"id": "1603714434"
}
]
}
],
"questions": [...],
"error": false
}Section 欄位說明:
sections[].id: Section ID(用於goToSection導航),預設區塊為nullsections[].title: Section 標題options[].goToSection: 選擇此選項後跳轉至的 Section ID
interface Form {
title: string;
description: string | null;
collectEmails: "NONE" | "VERIFIED" | "INPUT";
sections: FormSection[]; // 依 Section 分組
questions: Question[]; // 向下相容的扁平列表
error: false;
}
interface FormSection {
id: string | null; // Section ID,預設區塊為 null
title: string | null; // Section 標題
questions: Question[];
}
interface Question {
title: string;
description: string | null;
type: "TEXT" | "PARAGRAPH_TEXT" | "MULTIPLE_CHOICE" |
"CHECKBOXES" | "DROPDOWN" | "DATE" | "TIME" |
"SCALE" | "GRID" | "FILE_UPLOAD";
options: (string | QuestionOption)[]; // 可能是純字串或帶導航的物件
required: boolean;
id: string;
}
interface QuestionOption {
value: string;
goToSection?: string | null; // 目標 Section ID,null 表示繼續下一題
}Submit answers to the form using question IDs from the GET response.
Request:
curl -X POST http://localhost:8787/g/1FAIpQLSezfDEk03hYi9duf1vVSDGGBFAZq2zfPNw9_smS_8X2xmfzWQ \
-H "Content-Type: application/json" \
-d '{
"1536632002": "test@example.com",
"1132838313": "選項 1",
"216510093": ["選項 1", "選項 3"]
}'Request Body Format:
{
"<question_id>": "answer",
"<question_id>": ["answer1", "answer2"],
"emailAddress": "optional@email.com"
}Notes:
- Use question
idfrom GET response as keys - Single-choice questions: use string value
- Multi-choice questions (CHECKBOXES): use array of strings
- Optional: include
emailAddressif form collects emails
Success Response:
{
"error": false,
"message": "Form submitted successfully."
}Error Response:
{
"error": true,
"message": "Unable to submit the form. Check your form ID and email settings, and try again."
}Edit wrangler.jsonc:
- GET requests are cached for 60 seconds in-memory
- Cache is instance-local (not shared across edge locations)
- Helps reduce load on Google Forms
CORS headers are enabled by default:
access-control-allow-origin: *access-control-allow-methods: GET, POST, OPTIONSaccess-control-allow-headers: Content-Type
Common errors and solutions:
| Error | Cause | Solution |
|---|---|---|
| 404 | Invalid form ID | Verify form ID from URL |
| 502 | Cannot fetch form | Check form is public |
| 400 | Invalid submission data | Verify question IDs match |
wrangler.jsonc 中的 name 欄位:
{
"name": "your-custom-name", // ← 改成你想要的名稱
"main": "src/index.ts",
...
}這個名稱會成為你的 Worker URL:https://your-custom-name.<subdomain>.workers.dev
npx wrangler login這會開啟瀏覽器讓您登入 Cloudflare 帳號並授權 Wrangler CLI。
npm run deploy部署成功後會顯示您的 Worker URL:
✨ Success! Uploaded to Cloudflare
https://your-custom-name.<subdomain>.workers.dev
修改程式碼後,再次執行 npm run deploy 即可更新。
如果您有自己的網域,可以在 Cloudflare Dashboard 設定 Custom Domain:
- Workers & Pages → 選擇您的 Worker
- Settings → Triggers → Custom Domains
- 新增網域(例如:
api.yourdomain.com)
- Runtime: Cloudflare Workers
- Language: TypeScript
- Parser: Cheerio (HTML parsing)
- Build: Wrangler
MIT
Based on openform by eiiot. This project is a fork/extension of eiiot's work, simplified for general usage and enhanced with a Web UI.
Inspired by opensheet by Ben Borgers.
Special thanks to:
- eiiot for the original openform implementation.
- Ben Borgers for opensheet, which served as the foundation and inspiration for these types of serverless wrappers.
感謝:
{ "$schema": "node_modules/wrangler/config-schema.json", "name": "openform-worker", "main": "src/index.ts", "compatibility_date": "2024-01-01", "compatibility_flags": ["nodejs_compat"], "dev": { "port": 8787 } }