Chuyển đến nội dung chính
Một bài đăng xuất bản nội dung lên một hoặc nhiều nền tảng được kết nối với Profile. Bạn gửi một request, MADIAD Hub phân phối nội dung đến từng nền tảng bạn liệt kê và trả về kết quả theo từng nền tảng.
Đăng bài không giới hạn trên mọi gói trả phí, không có hạn mức số bài đăng hằng tháng. Mức sử dụng vẫn được ghi nhận, và mỗi request được tính là một lượt đăng bất kể nhắm tới bao nhiêu nền tảng: đăng lên TikTok, Instagram và Facebook trong một lần gọi được ghi nhận 1 lượt đăng, không phải 3. Xem Mức sử dụng để đọc số liệu hiện tại.

Chọn endpoint theo loại media

Endpoint ảnh dùng multipart/form-data khi tải file lên, hoặc JSON thuần khi ảnh đến từ URL (photo_urls[], xem Đăng ảnh bằng URL). Văn bản và video dùng JSON.

Số ảnh mỗi bài

MADIAD Hub không tự đặt giới hạn số ảnh bạn đính kèm vào photos[], ảnh được chuyển thẳng qua nền tảng. Mỗi nền tảng tự áp giới hạn tối đa riêng, nên giới hạn thực tế cho một bài fan-out là giới hạn nhỏ nhất trong số các nền tảng bạn nhắm tới:
Đây là giới hạn do chính các nền tảng công bố, không phải giới hạn MADIAD Hub áp đặt, các con số này có thể thay đổi phía nền tảng mà không báo trước. Nếu chưa chắc, hãy thử với một batch nhỏ trước.

Phân phối với platforms[]

Liệt kê tất cả nền tảng đích. Nội dung giống nhau được gửi đến từng nền tảng, trừ khi bạn ghi đè theo từng nền tảng.
Một request dùng chung phần media cho tất cả nền tảng được liệt kê. Bạn có thể ghi đè phần chữ theo từng nền tảng (caption → <platform>_caption, xem bên dưới) và truyền tuỳ chọn riêng của nền tảng, nhưng photos[] / photo_urls[] / video bạn đính kèm sẽ đi tới mọi nền tảng trong request đó, không có field media riêng cho từng nền tảng. Muốn đăng ảnh hoặc video khác nhau cho mỗi nền tảng, hãy gửi một request riêng cho từng nền tảng (mỗi request kèm photos[]/video của riêng nó).

Đăng ảnh bằng URL

Thay vì tải file lên, bạn có thể truyền link công khai qua photo_urls[]. MADIAD Hub sẽ tự tải từng ảnh về phía máy chủ và đăng y hệt như khi bạn gửi file trực tiếp. Nên dùng cách này cho ảnh dung lượng lớn hoặc nhiều ảnh một lúc: phần truyền tải nặng diễn ra ở phía chúng tôi, request của bạn luôn gọn nhẹ và không bao giờ gặp lỗi vượt kích thước request.
  • Mỗi URL phải là link ảnh trực tiếp, công khai, dùng https:// (hostname, không dùng địa chỉ IP). Link trang chia sẻ mở ra một trang web (ví dụ trang xem của drive) sẽ bị từ chối kèm thông báo lỗi rõ ràng. Hãy dùng URL file trực tiếp. Redirect được theo tối đa 5 bước, nhưng mọi bước đều phải là https://.
  • Tối đa 40 URL mỗi request, 30 MB mỗi ảnh và tổng 120 MB.
  • Cũng dùng được trong multipart: gửi photo_urls[] dưới dạng field text, và trộn thoải mái với photos[] dạng file trong cùng một request. Với carousel, file tải lên đứng trước, rồi tới ảnh từ URL, mỗi nhóm theo thứ tự gửi.

Ghi đè theo từng nền tảng

caption là chú thích chung mặc định. Ghi đè cho một nền tảng cụ thể bằng field <platform>_caption, tiện lợi cho hashtag, mention hoặc giới hạn độ dài.
Cùng kiểu tiền tố này áp dụng cho các tùy chọn theo nền tảng khác, chẳng hạn instagram_first_comment.
Ghi đè cho Instagram được đăng thành một job riêng. Khi instagram_caption khác với caption chung và request còn nhắm tới nền tảng khác, MADIAD Hub gửi Instagram tách riêng để nó nhận đúng nội dung bạn viết. Với bài đăng thường, bạn không thấy gì khác biệt: vẫn một lượt đăng tính vào hạn mức, một request_id, một trạng thái để theo dõi. Với bài hẹn giờ thì có một điểm nhìn thấy được: danh sách lịch đăng hiển thị Instagram thành một mục riêng, nên huỷ hoặc sửa nó là thao tác tách khỏi các nền tảng còn lại.Bài ảnh và video lên Google Business cũng vậy. Nội dung riêng của Google Business (google_business_caption, hoặc caption đã cắt còn 1.500) được gửi thành một job Google Business riêng, vì với hai loại bài này nền tảng chỉ đọc nội dung chung. Bài văn bản lên Google Business vẫn nằm chung một job.Các nền tảng dạng bài viết cũng vậy: WordPress, Dev.to, Hashnode, Lemmy, Listmonk và Whop. Ở các nền tảng này, <platform>_title là tiêu đề bài viết còn <platform>_caption là phần thân bài. Khi wordpress_caption khác với caption chung, nó được gửi thành một job WordPress riêng để bài viết mang đúng nội dung đó chứ không phải nội dung chung. Quy tắc add_to_queue bên dưới cũng áp dụng cho các nền tảng này.Có một tổ hợp bị từ chối: add_to_queue đi kèm instagram_caption khác với caption chung, khi request chọn Instagram cùng các nền tảng khác. Khi đó Instagram sẽ chiếm một slot hàng đợi riêng và lên vào thời điểm khác với phần còn lại, nên request bị từ chối thay vì đăng lệch giờ. Hãy đưa Instagram vào hàng đợi bằng một request riêng, hoặc dùng caption chung. google_business_caption trên bài ảnh, video cũng vậy. Nếu request chỉ chọn riêng nền tảng đó thì không cần job riêng: caption của nó được dùng làm nội dung bài và vào hàng đợi bình thường.

Giới hạn ký tự

caption ánh xạ vào trường văn bản chính của mỗi nền tảng. Giới hạn khác nhau theo nền tảng, và ở một vài nền tảng trường đó là tiêu đề ngắn, còn nội dung dài nằm ở description riêng: Một số quy tắc rút ra từ bảng trên:
  • caption dài quá giới hạn của nền tảng nào thì chỉ bị cắt cho riêng nền tảng đó. Các nền tảng khác vẫn nhận caption đầy đủ, và request không bị từ chối. Quy tắc này áp cho tiêu đề (YouTube, Pinterest, Reddit, bài ảnh TikTok), cho phần thân bài (Instagram, LinkedIn, Google Business, video TikTok, Mastodon), và cho phần mô tả lấy từ caption (Pinterest 500, YouTube 5.000 byte, bài ảnh TikTok 4.000). Tiêu đề được gộp thành một dòng; thân bài giữ nguyên xuống dòng. Chỗ cắt rơi vào cuối một từ nếu vẫn giữ được ít nhất 60% giới hạn, không bao giờ cắt đôi emoji, và kết thúc bằng một dấu ”…”.
  • Phản hồi cho biết đã cắt những gì. Mảng truncated liệt kê từng nền tảng và phần chữ bị cắt (text: title, caption hoặc description), kèm length của caption và limit. Nền tảng nào không có trong danh sách thì đã nhận đủ caption. Muốn tự chọn nội dung, hãy đặt trường riêng của nền tảng đó: <platform>_caption (hoặc <platform>_title), hay các trường mô tả trong bảng trên.
  • Nội dung bạn đặt riêng cho một nền tảng thì không bao giờ bị cắt. <platform>_title hay <platform>_caption bạn gửi được đăng đúng như bạn viết. Vì vậy, nếu nó dài quá giới hạn của nền tảng đó, request bị từ chối kèm lỗi nêu rõ trường. Các trường mô tả bạn tự đặt (description, pinterest_description, youtube_description, tiktok_description) cũng được gửi đúng như bạn viết, và không được kiểm tra độ dài trước khi đăng.
  • Một ngoại lệ: bài Instagram (và bài ảnh, video Google Business hoặc Mastodon) vào hàng đợi. Phần chữ đã cắt cho Instagram đi trong một job Instagram riêng (xem ghi chú ở trên), và với bài ảnh, video thì Google Business và Mastodon cũng vậy. Vì thế request có add_to_queue, chọn một trong hai nền tảng này cùng các nền tảng khác và caption dài quá giới hạn của nó sẽ bị từ chối. Hãy rút ngắn caption, hoặc đưa nền tảng đó vào hàng đợi bằng một request riêng. Nếu chỉ đưa riêng nền tảng đó vào hàng đợi thì caption vẫn được cắt và xếp lịch bình thường.
  • Trên dashboard MADIAD Hub, bộ đếm dưới ô Nội dung đo theo giới hạn thấp nhất trong các nền tảng bạn đã chọn. Chưa chọn nền tảng nào thì bộ đếm đo theo mọi nền tảng đã kết nối với profile. Ngay dưới đó là danh sách nền tảng sẽ bị cắt, cắt còn bao nhiêu ký tự. Đây chỉ là thông báo; bài vẫn đăng bình thường. Trình soạn bài còn cho bạn chọn viết một nội dung riêng cho từng nền tảng, thay vì một caption chung (“Riêng từng nền tảng”). Khi đó mỗi nền tảng đã chọn có một ô riêng và được gửi nội dung trong ô đó. Hub không cắt chữ này, trừ một trường hợp: với bài Pinterest, bài YouTube và bài ảnh TikTok, ô tiêu đề để trống sẽ được điền một tiêu đề cắt từ chính nội dung của nền tảng đó. Nội dung vượt giới hạn cứng của nền tảng sẽ bị từ chối kèm tên ô trước khi gửi đi, và ô để trống cũng bị từ chối. Ô của Mastodon được gửi đúng như đã gõ, giống mastodon_caption.
  • Facebook, X, Threads và Bluesky không bao giờ bị cắt. X, Threads và Bluesky tự tách văn bản dài thành chuỗi bài, giới hạn của Facebook là 63.206, còn Telegram và Discord tự cắt ở phía họ.
  • Phần ghi đè theo từng nền tảng được kiểm tra, với những nền tảng có giới hạn cứng: Instagram, LinkedIn, TikTok, YouTube, Reddit, Pinterest và Google Business. Một <platform>_title (hoặc <platform>_caption) vượt giới hạn (ví dụ tiktok_title > 90 cho bài ảnh, reddit_title > 300, hay instagram_caption > 2.200) sẽ bị báo lỗi rõ ràng trước khi đăng. X, Threads và Bluesky không kiểm trước vì chúng tự tách thành chuỗi bài thay vì từ chối; Telegram và Discord thì cắt bớt; còn ghi đè cho Facebook chỉ bị giới hạn ở Reels. Những trường hợp này được gửi thẳng tới nền tảng và tự báo kết quả riêng.
  • Link trên X. Mọi URL mà X biến thành liên kết bấm được sẽ bị gỡ khỏi bài X trước khi đăng. Hãy để link trong hồ sơ hoặc trong ảnh/video.

Tùy chọn nâng cao theo nền tảng

Bất kỳ tùy chọn nào một nền tảng hỗ trợ đều có thể truyền qua, MADIAD Hub chuyển tiếp nguyên vẹn, nên bạn không bị giới hạn ở các field liệt kê ở đây. Thêm tiền tố tên nền tảng khi tùy chọn đó đặc thù cho nền tảng. Một vài ví dụ phổ biến: Các field dạng mảng dùng quy ước field[], lặp lại key một lần cho mỗi giá trị. Để xem toàn bộ các tùy chọn theo từng nền tảng (mức độ riêng tư, loại video, poll, CTA, tải phụ đề và nhiều hơn nữa), xem Tùy chọn theo nền tảng.

Bình luận đầu tiên (first comment)

Tự động đăng một bình luận ngay khi bài vừa xuất bản, tiện để gắn link, hashtag hoặc lời kêu gọi hành động mà bạn không muốn nhét vào caption chính. Đặt first_comment làm giá trị chung, và ghi đè theo từng nền tảng bằng <platform>_first_comment (giá trị riêng từng nền tảng được ưu tiên khi có cả hai).
Hỗ trợ trên Instagram, Facebook, Threads, Bluesky, Reddit, X, YouTube và LinkedIn. Một vài hành vi riêng theo nền tảng:
  • X và Threads: đăng dưới dạng reply cho bài. Với bài X tự tách chuỗi, nó reply vào tweet cuối của chuỗi.
  • YouTube: đăng thành bình luận cấp cao nhất dưới video.
  • Instagram: chỉ áp dụng cho bài ảnh và video (Instagram không có bài chỉ văn bản).
Để đính ảnh vào bình luận, gửi first_comment_media[] dạng file nhị phân, y hệt photos[]. Hiện chỉ hỗ trợ Reddit, và không dùng được cho bài đã lên lịch hoặc đang trong hàng đợi.

Caption sinh bằng AI

Để MADIAD Hub tự viết caption thay vì bạn cung cấp. Hữu ích nhất với bài ảnh và video:

Yêu cầu của từng nền tảng

Bài đăng Facebook đi đến một Page, không phải Profile cá nhân. Nếu Profile chỉ có một Page thì tự động dùng Page đó; nếu có nhiều Page, truyền facebook_page_id để chọn. Để liệt kê các Page của một Profile và lấy ID của chúng, dùng GET /v1/connections/facebook/pages, xem Tìm ID của Page & board. Nếu Profile đang ghim một Page thì Page đó thắng facebook_page_id, xem Ghim một Page vào Profile.
Gửi ảnh dưới dạng file nhị phân (photos[]=@file) hoặc link công khai (photo_urls[]), xem Đăng ảnh bằng URL. Với video, truyền một video_url công khai.
Bài đăng Reddit luôn có tiêu đề. Mặc định caption được dùng làm tiêu đề; truyền reddit_title nếu muốn đặt tiêu đề riêng.

Đọc response

Lệnh đăng trả về status của bài, các nền tảng đã nhắm tới, và các id để bạn poll:
  • completed: mọi nền tảng đã đăng bài, results chứa kết quả theo từng nền tảng. Ở response của lệnh đăng, trạng thái này nghĩa là tất cả đã trả lời ngay.
  • processing: đã được tiếp nhận và đang được đăng; đây là câu trả lời đầu tiên thông thường với mọi loại bài. Poll bằng request_id, hoặc đăng ký webhooks.
  • scheduled: đã lên lịch cho thời điểm tương lai, poll bằng job_id. job_id chỉ có ở bài gửi để đăng sau: đó là id dùng để hủy hoặc dời bài.
  • partial: một số nền tảng đã đăng thành công và ít nhất một nền tảng bị từ chối, kiểm tra từng mục trong results.
  • draft: không nền tảng nào từ chối, nhưng ít nhất một nền tảng nhận bài vào mục nháp của tài khoản thay vì đăng, nên ở đó bài chưa hiện công khai. Thường gặp nhất là TikTok: khi tài khoản chạm giới hạn đăng trực tiếp trong ngày của TikTok, bài vào hộp thư của ứng dụng TikTok và chờ chủ tài khoản bấm đăng. Mục của nền tảng đó trong results có "draft": true, success: true và không có url. Đây là kết quả cuối: đừng gửi lại, gửi lại chỉ thêm một bản nháp nữa.
  • failed: tất cả nền tảng nhắm tới đều từ chối bài đăng. Mã HTTP vẫn là 200, lý do theo từng nền tảng nằm trong results.
  • canceled: bài hẹn giờ bạn đã hủy trước khi nó chạy. Không có gì được đăng, results là null, và trạng thái này không đổi nữa. Lệnh đăng không bao giờ trả trạng thái này; lệnh tra trạng thái và lịch sử bài đăng thì có.
  • unknown: chưa xác định được kết quả trong thời gian chờ (trả về HTTP 502 với mã upstream_timeout). Bài có thể đã lên, đừng retry ngay. Câu trả lời có kèm request_id: tra trạng thái bằng mã đó sẽ nhận unknown cho tới khi phía nền tảng nhận được bài, sau đó là processing rồi kết quả. Xem Lỗi và Idempotency.
Poll trạng thái (GET /v1/posts/status) dùng cùng bộ giá trị này.
Response đầu tiên thường là processing, với bài chữ và bài ảnh cũng như video. Lệnh đăng thường trả lời processing kèm request_id, và kết quả lấy từ poll trạng thái hoặc webhook. Có hai trường hợp đã là kết quả cuối ngay ở response đầu:
  • failed: mọi nền tảng bạn chọn đều từ chối bài. Không có gì được đăng, lượt đăng không bị tính, và bạn có thể gửi lại cùng Idempotency-Key sau khi sửa nội dung. Response vẫn có thể kèm request_id.
  • scheduled: bài đã được tiếp nhận cho thời điểm sau (scheduled_at hoặc add_to_queue). Response đó chưa phải kết luận nào; thường nó mang cả job_id lẫn request_id.
Khi có results, nó được phân theo từng nền tảng. Mỗi mục có success, kèm publish_id và url khi bài đã lên, hoặc error và error_code khi nền tảng từ chối. Mục có "draft": true là bài đã vào mục nháp của tài khoản và không có url (xem draft ở trên):
error là câu của chính nền tảng. Nếu MADIAD Hub có bản dịch tiếng Việt cho câu đó, bản dịch nằm sau · Tạm dịch:, ở response của lệnh đăng lẫn khi poll trạng thái, cùng dạng với error.message (xem Lỗi). error_code là mã lý do ngắn gọn dành cho máy đọc (ví dụ account_restricted, daily_cap_reached, media_invalid_format), hãy phân nhánh theo mã này thay vì theo câu chữ của error. Mã chỉ xuất hiện khi nền tảng có trả về. Response của lệnh đăng và kết quả poll trạng thái dùng đúng cùng một cấu trúc này, trừ một trường: truncated (những gì đã được cắt cho vừa, xem mục Giới hạn ký tự ở trên) chỉ có trong response của lệnh đăng. Khi một số (nhưng không phải tất cả) nền tảng thất bại, status cuối cùng là partial. Ở response của lệnh đăng, mảng failed_platforms liệt kê các nền tảng được báo là thất bại cho đến lúc này, thường đi kèm processing; poll trạng thái sau đó báo partial, với từng lần từ chối nằm trong results.
Một bài đăng có thể thành công một phần, khi một nền tảng xuất bản được còn nền tảng khác thất bại. Hãy kiểm tra results theo từng nền tảng thay vì chỉ dựa vào status ở cấp cao nhất.

Cảnh báo: bài đã lên nhưng chưa đúng hẳn như yêu cầu

Có lúc nền tảng nhận bài nhưng bỏ qua một phần bạn gửi. Ví dụ: bài ảnh TikTok có chọn nhạc bằng tiktok_music_id, mà kết nối TikTok của Profile đó chưa có quyền dùng nhạc, thì bài vẫn lên (hoặc được lưu nháp, với post_mode: MEDIA_UPLOAD) nhưng không có nhạc. status vẫn giữ nguyên như khi không có cảnh báo, nên chỉ mảng warnings cho bạn biết chuyện này:
  • warnings chỉ xuất hiện khi có ít nhất một cảnh báo. Bài không có cảnh báo thì không có khoá warnings.
  • code là music_not_applied (chỉ TikTok: kết nối lại TikTok cho Profile đó), first_comment_skipped (first_comment không được đăng, ví dụ vì bài được lưu nháp) hoặc platform_warning (lưu ý khác, đọc message). Có thể có thêm mã mới, nên gặp mã lạ thì xử lý như platform_warning.
  • platform là null khi lệnh đăng nhiều kênh trả về cảnh báo mà không nêu kênh nào.
  • Cảnh báo không có nghĩa là bài lỗi: đừng đăng lại bài chỉ vì một cảnh báo.
  • Với bài nhận processing, cảnh báo đến cùng kết quả poll trạng thái, không có ở response đầu tiên của lệnh đăng. Cảnh báo chỉ trả kèm câu trả lời đầu tiên của bài thì chỉ có ở response của lệnh đăng, nên hãy lưu lại từ đó.

Video: trả lời ngay, đăng ở chế độ nền

Một video có thể khiến nền tảng xử lý cả phút hoặc lâu hơn. Mặc định, lệnh đăng chờ nền tảng một lúc, và bài nào chậm sẽ kết thúc bằng 502 upstream_timeout (unknown). Gửi "async_upload": true trong POST /v1/posts/video thì lệnh trả lời trong vài giây: luôn là processing kèm request_id, không bao giờ kèm results của từng nền tảng. Kết quả lấy từ lệnh tra trạng thái hoặc webhook. Lỗi của chính request (đường dẫn không phải video, thiếu tiêu đề YouTube) vẫn được trả ngay. request_id luôn do MADIAD Hub đặt. Giá trị bạn gửi trong body sẽ bị bỏ qua.

Kiểm tra trạng thái sau

Poll một bài bất đồng bộ hoặc đã lên lịch bằng request_id hoặc job_id từ response của lệnh đăng:
Để nhận cập nhật chủ động thay vì kiểm tra định kỳ, xem Webhooks.

Bài tài liệu (LinkedIn)

Kênh áp dụng: chỉ LinkedIn. Định dạng: PDF, PPT, PPTX, DOC, DOCX.
LinkedIn hiển thị PDF, PPT, PPTX, DOC, DOCX thành một tài liệu lật được ngay trong bảng tin. Đây là chỗ duy nhất trên các kênh đang kết nối mà một tệp nhiều trang là nội dung thật sự, không phải cái link.
Cách đặt tên trường ở đây đọc hơi lạ lần đầu: caption là TIÊU ĐỀ tài liệu hiện trên thẻ, còn description là đoạn chữ phía trên. Cả hai đều theo cách LinkedIn hiển thị. caption là bắt buộc.Chỉ LinkedIn nhận loại bài này, nên platforms phải đúng ["linkedin"]. Tối đa 100 MB, 300 trang.

Đăng lại phần bị lỗi

Kênh áp dụng: mọi kênh. Retry chỉ gửi lại đúng những kênh đã lỗi trong bài đó.
Khi một bài đi nhiều kênh mà chỉ vài kênh lỗi, gọi retry để gửi lại đúng những kênh đã lỗi. Tệp không phải tải lên lần nữa và không bị tính thêm lượt đăng: lượt đó đã trừ khi bài được nhận lần đầu, còn một lần giao không thành thì không phải một lần giao mới.
Truyền đúng một trong request_id hoặc job_id. Phản hồi có mảng legs, vì một bài bị tách caption là hai việc ở tầng dưới, và cả hai đều được thử lại. Kênh nào đã thành công trả về “không có gì để thử lại”; đó là câu trả lời bình thường, không phải lỗi. Đừng gửi lại một bài lỗi một phần bằng cách gọi lại endpoint đăng bài với cùng platforms: đó là một bài mới, và bài sẽ lên lần thứ hai ở mọi nền tảng đã đăng thành công. Trên dashboard, nút Gửi lại kênh lỗi cạnh bài ở trang Tổng quan làm đúng việc của lệnh gọi này; trang đó chỉ hiện các hoạt động gần nhất.

Gỡ bài đã đăng

post_id là id của nền tảng, không phải request_id của chúng tôi. Nhờ vậy gỡ được cả bài bạn đã đăng bằng tay, không qua Hub. Đó là publish_id trong kết quả của bài. Với Google Business, id là tên đầy đủ của bài, accounts/…/locations/…/localPosts/…; chỉ gửi dãy số thì trả 400. Phản hồi có trường removal, cho biết Hub biết chắc đến đâu: Chưa kiểm được: x, linkedin. Hai kênh này nhận lệnh gỡ cả khi id bài không tồn tại, nên 200 ở đây chưa chứng minh bài đã mất: hãy mở bài để xem. Ở các kênh còn lại, khi chúng tôi đo, id mà nền tảng không biết đều bị từ chối (400 ở Facebook, 404 not_found ở YouTube, Pinterest và Google Business). confirmed là lời báo của chính nền tảng. Hub không tự đọc lại bài. Bài Google Business chỉ được gỡ khi nằm ở một địa điểm của chính Profile đó; địa điểm khác trả 404 not_found.
Gỡ là không hoàn tác được trên nền tảng. Chúng tôi không đăng lại giúp bạn.Hỗ trợ: facebook, youtube, x, linkedin, pinterest, google_business. Instagram và TikTok không có endpoint xoá, và bài Threads cũng chưa gỡ được qua Hub: bài ở ba kênh này chỉ gỡ được trong ứng dụng. Gửi ba kênh này trả 400 ngay tại chỗ.