上传一个包含 Markdown 和本地图片的 ZIP,创建异步导入任务。系统会自动处理图片、修正引用关系,并在任务完成后返回适合预览、分发和继续加工的 HTML 内容。
围绕 markdown2html 场景设计,不是通用文档页拼装
面向 Obsidian、知识库导出的 Markdown 文件包,保留相对图片引用,并通过任务队列稳定处理大包导入。
创建任务并轮询状态后,即可拿到可预览、分发或接入工作流的 HTML 结果,减少人工二次整理。
输出风格可以保持统一,适合品牌内容、知识库发布和批量生产场景。
文章和图片资源会按清晰目录归档,后续检索、复用和协作都更顺手。
先把 Obsidian、知识库或本地整理好的 Markdown 文章包统一打成 ZIP 并上传。
再创建异步导入任务,让系统自动处理图片上传、链接替换和排版转换。
轮询任务完成后,将结果继续用于网页发布、内容分发、AI 加工或内部编辑流程。
下面这部分既是开发者控制台,也是可以直接复制给 AI 的异步接入文档。
一键申请 API Key,复制下面这份 Markdown 指南,交给 AI 或开发同事即可按异步流程直接对接。
申请导入 API Key
生成后只展示一次,后续只能吊销重建。
已生成的 Key
暂无可用 API Key
主题 API 标识
接口里的 themeId 与编辑器中的主题名称保持一致,下面同时给出可读名称和颜色参考。
AI 可读 API 对接文档
公众号https://api.imgto.link
POST https://api.imgto.link/uploads/md-importPOST https://api.imgto.link/api/v1/md/importGET https://api.imgto.link/api/v1/md/import/jobs/{jobId}https://api.imgto.link host, so clients only need one public base URL.Use an API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
uploadRelativePathPOST https://api.imgto.link/api/v1/md/importGET https://api.imgto.link/api/v1/md/import/jobs/{jobId} until the job reaches a terminal statusPOST https://api.imgto.link/uploads/md-import
multipart/form-data
archive (required): ZIP file containing one Markdown file and its local image files{
"code": 200,
"data": {
"relativePath": "inbox/2026/03/12/uuid-article-package.zip",
"originalFilename": "article-package.zip",
"size": 345678,
"uploadedAt": "2026-03-12T08:00:00.000Z"
}
}
POST https://api.imgto.link/api/v1/md/import
application/json
{
"uploadRelativePath": "inbox/2026/03/12/uuid-article-package.zip",
"archiveFilename": "article-package.zip",
"archiveSize": 345678,
"themeId": "classic",
"directoryPath": "public/articles/demo",
"entryMarkdown": "docs/index.md",
"saveFolderId": 12,
"mdfileId": 123
}
uploadRelativePath (required): value returned from step 1archiveFilename (required): original zip filenamearchiveSize (optional): archive size in bytesthemeId (optional): theme API name, default is classicdirectoryPath (optional): base upload directory path like public/articles/launch-2026entryMarkdown (optional): specific markdown file path inside the ZIP when there are multiple markdown filessaveFolderId (optional): target markdown folder ID in the editormdfileId (optional): existing markdown article ID. When provided, the import is treated as a replace/update. The old article and its imported images are deleted before the new content is saved{
"code": 202,
"message": "Accepted",
"data": {
"jobId": "mdi_1234567890abcdef",
"status": "QUEUED",
"progress": 0,
"pollUrl": "/api/v1/md/import/jobs/mdi_1234567890abcdef"
}
}
GET https://api.imgto.link/api/v1/md/import/jobs/{jobId}
{
"code": 200,
"message": "Ok",
"data": {
"jobId": "mdi_1234567890abcdef",
"status": "PROCESSING",
"progress": 65,
"currentStage": "processing_images",
"message": "正在处理图片 2/4",
"compensationStatus": "NOT_REQUIRED",
"updatedAt": "2026-03-12T08:01:15.000Z"
}
}
{
"code": 200,
"message": "Ok",
"data": {
"jobId": "mdi_1234567890abcdef",
"status": "SUCCEEDED",
"progress": 100,
"currentStage": "completed",
"message": "导入完成",
"compensationStatus": "NOT_REQUIRED",
"updatedAt": "2026-03-12T08:01:30.000Z",
"result": {
"mdfileId": 123,
"resolvedMarkdown": "# Title\n\n",
"html": "<section>...</section>",
"wechatApiHtml": "<section>...</section>",
"imageDirectoryPath": "public/公众号/my-article",
"savedArticle": {
"id": 123,
"mdfileId": 123,
"title": "My Article",
"folder": {
"id": 9,
"name": "公众号",
"fullPath": "公众号"
}
}
}
}
}
{
"code": 200,
"message": "Ok",
"data": {
"jobId": "mdi_1234567890abcdef",
"status": "FAILED_COMPENSATED",
"progress": 100,
"currentStage": "failed",
"message": "导入失败,已回滚上传图片与用户额度",
"compensationStatus": "COMPLETED",
"updatedAt": "2026-03-12T08:02:00.000Z",
"error": {
"message": "Referenced image not found: ./images/cover.png"
}
}
}
SUCCEEDEDFAILED_COMPENSATEDFAILED_PENDING_COMPENSATION and FAILED_COMPENSATION_ERRORresult is returned only when the job succeedserror is returned when the job failsresult.resolvedMarkdown: Markdown after local image paths are replaced with Imgto.link URLsresult.html: Base rendered HTML for preview or debuggingresult.wechatApiHtml: Recommended HTML for submitting to the WeChat Official Account API content fieldresult.mdfileId: The saved Markdown article ID, returned for convenient reuse in the next replace import requestresult.savedArticle: Saved Markdown article record in the editorresult.savedArticle.mdfileId: Stable article identifier to pass back on the next import if you want to replace this article instead of creating a new oneresult.imageDirectoryPath: Final image upload directory pathcurl -X POST "https://api.imgto.link/uploads/md-import" \
-F "archive=@article-package.zip" \
curl -X POST "https://api.imgto.link/api/v1/md/import" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uploadRelativePath": "inbox/2026/03/12/uuid-article-package.zip",
"archiveFilename": "article-package.zip",
"archiveSize": 345678,
"themeId": "classic",
"directoryPath": "public/articles/demo"
}'
curl -X GET "https://api.imgto.link/api/v1/md/import/jobs/mdi_1234567890abcdef" \
-H "Authorization: Bearer YOUR_API_KEY"
const uploadFormData = new FormData();
uploadFormData.append("archive", zipFile);
const uploadResponse = await fetch("https://api.imgto.link/uploads/md-import", {
method: "POST",
body: uploadFormData
});
const uploadResult = await uploadResponse.json();
const relativePath = uploadResult.data.relativePath;
const createResponse = await fetch("https://api.imgto.link/api/v1/md/import", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
uploadRelativePath: relativePath,
archiveFilename: uploadResult.data.originalFilename,
archiveSize: uploadResult.data.size,
themeId: "classic",
directoryPath: "public/articles/demo"
})
});
const createResult = await createResponse.json();
const jobId = createResult.data.jobId;
let finalResult = null;
for (;;) {
const pollResponse = await fetch("https://api.imgto.link/api/v1/md/import/jobs/" + jobId, {
method: "GET",
headers: {
Authorization: "Bearer YOUR_API_KEY"
}
});
const pollResult = await pollResponse.json();
const job = pollResult.data;
if (job.status === "SUCCEEDED") {
finalResult = job.result;
break;
}
if (job.status === "FAILED_COMPENSATED") {
throw new Error(job.error?.message || "Import failed");
}
await new Promise((resolve) => setTimeout(resolve, 1800));
}
console.log(finalResult.mdfileId);
console.log(finalResult.resolvedMarkdown);
console.log(finalResult.wechatApiHtml);
console.log(finalResult.savedArticle);
Use the result.savedArticle.mdfileId returned by the previous successful import job.
const replaceResponse = await fetch("https://api.imgto.link/api/v1/md/import", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
uploadRelativePath: uploadedZip.data.relativePath,
archiveFilename: uploadedZip.data.originalFilename,
archiveSize: uploadedZip.data.size,
themeId: "classic",
mdfileId: previousResult.savedArticle.mdfileId
})
});
const replaceResult = await replaceResponse.json();
console.log(replaceResult.data.jobId);
mdfileId omitted: create a new import job using the uploaded ZIPmdfileId provided: force this import to replace that exact articlemdfileId from the final successful poll result and keep using it for future updatesUse result.wechatApiHtml from the completed import job for the content field instead of result.html.
const completedImport = await waitForImportJob(jobId);
const wechatApiHtml = completedImport.result.wechatApiHtml;
const draftPayload = {
articles: [
{
title: completedImport.result.savedArticle?.title || "Imported article",
author: "Your Team",
digest: "",
content: wechatApiHtml,
content_source_url: "",
thumb_media_id: "YOUR_WECHAT_THUMB_MEDIA_ID",
need_open_comment: 0,
only_fans_can_comment: 0
}
]
};
const wechatResponse = await fetch(
"https://api.weixin.qq.com/cgi-bin/draft/add?access_token=YOUR_WECHAT_ACCESS_TOKEN",
{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(draftPayload)
}
);
const wechatResult = await wechatResponse.json();
console.log(wechatResult);
result.wechatApiHtml for WeChat Official Account API publishingresult.html as the content field if you need output aligned with the website's copy/export flowresult.wechatApiHtml should already point to uploaded Imgto.link assetsaccess_token and thumb_media_idwarm): #ea580c, #333china): #7f1d1d, #292524, #f5f5f4cyber): #ec4899, #a855f7, #3b82f6classic): #07c160, #333ink): #3f3f46, #78716ceditorial): #1f2937, #ef4444wenkai): #57534e, #78716cbusiness): #1e3a8a, #1e40afforest): #059669, #ecfdf5azure): #0ea5e9, #e0f2fecoffee): #78350f, #fffbebneon): #d946ef, #06b6d4red): #be123c, #fff1f2silver): #475569, #e2e8f0minimal): #000, #999morandi): #be185d, #fbcfe8sunny): #f97316, #fbbf24mint): #10b981, #d1fae5autumn): #d97706, #fef3c7lavender): #8b5cf6, #ede9fevintage): #7f1d1d, #fecacanotes): #facc15, #fef08agrey): #374151, #f3f4f6entryMarkdown./images/a.png公众号{directoryPath}/{article-slug}If you give this API to an AI coding assistant, tell it:
"Use the async workflow. First upload the ZIP to https://api.imgto.link/uploads/md-import with multipart/form-data and read data.relativePath, data.originalFilename, and data.size. Then call POST https://api.imgto.link/api/v1/md/import with JSON using uploadRelativePath, archiveFilename, and optional themeId, directoryPath, entryMarkdown, saveFolderId, and mdfileId. Authenticate the control-plane API with Authorization: Bearer YOUR_API_KEY. Then poll GET https://api.imgto.link/api/v1/md/import/jobs/{jobId} until status becomes SUCCEEDED or FAILED_COMPENSATED. On success, read result.resolvedMarkdown, result.wechatApiHtml, result.savedArticle, result.mdfileId, and result.imageDirectoryPath. Prefer result.wechatApiHtml when publishing to the WeChat Official Account API content field."